Removing arrow indicator from homescreen and

adding a separate view for various accessibility actions

Bug: 159247871
Change-Id: I14536844929e03d36a6a5f294d4f5daad8c82d15
This commit is contained in:
Sunny Goyal 2020-07-20 12:38:01 -07:00
parent 0edfbc64d4
commit 52e2545a66
17 changed files with 137 additions and 565 deletions

View File

@ -215,10 +215,8 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
switch (state.ordinal) {
case HINT_STATE_ORDINAL: {
Workspace workspace = getWorkspace();
boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
getStateManager().goToState(NORMAL, true,
willMoveScreens ? null : getScrimView()::startDragHandleEducationAnim);
if (willMoveScreens) {
getStateManager().goToState(NORMAL);
if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) {
workspace.post(workspace::moveToDefaultScreen);
}
break;

View File

@ -38,11 +38,9 @@ import android.widget.FrameLayout;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.systemui.plugins.PluginListener;
@ -126,12 +124,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
}
anim.play(ObjectAnimator.ofFloat(
mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt(
mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0);
dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
anim.play(dragHandleAnim);
return anim;
}

View File

@ -87,20 +87,6 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLaunc
});
}
if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == ALL_APPS) {
if (incrementEventCount(ALL_APPS_COUNT)) {
stateManager.removeStateListener(this);
mLauncher.getScrimView().updateDragHandleVisibility();
}
}
}
});
}
if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
HOTSEAT_DISCOVERY_TIP_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {

View File

@ -44,7 +44,6 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
@ -77,15 +76,11 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
private final float mRadius;
private final int mMaxScrimAlpha;
private final Paint mPaint;
private final OnboardingPrefs mOnboardingPrefs;
// Mid point where the alpha changes
private int mMidAlpha;
private float mMidProgress;
// The progress at which the drag handle starts moving up with the shelf.
private float mDragHandleProgress;
private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
@ -103,7 +98,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
private boolean mRemainingScreenPathValid = false;
private Mode mSysUINavigationMode;
private boolean mIsTwoZoneSwipeModel;
public ShelfScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@ -112,7 +106,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mEndAlpha = Color.alpha(mEndScrim);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOnboardingPrefs = mLauncher.getOnboardingPrefs();
// Just assume the easiest UI for now, until we have the proper layout information.
mDrawingFlatColor = true;
@ -145,11 +138,9 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
// Show the shelf more quickly before reaching overview progress.
mBeforeMidProgressColorInterpolator = ACCEL_2;
mAfterMidProgressColorInterpolator = ACCEL;
mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
} else {
mBeforeMidProgressColorInterpolator = ACCEL;
mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
mIsTwoZoneSwipeModel = false;
}
}
@ -164,7 +155,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
Context context = getContext();
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mDragHandleProgress = 1;
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
&& SysUINavigationMode.removeShelfFromOverview(context)) {
// Fade in all apps background quickly to distinguish from swiping from nav bar.
@ -182,29 +172,22 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
+ hotseatPadding.bottom + hotseatPadding.top;
float dragHandleTop =
Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mDragHandleSize.y;
mTopOffset = dp.getInsets().top;
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
@Override
public void updateColors() {
super.updateColors();
mDragHandleOffset = 0;
if (mDrawingFlatColor) {
return;
}
if (mProgress < mDragHandleProgress) {
mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress);
}
if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
mShelfTop = mShiftRange * mProgress + mTopOffset;
} else {
@ -258,20 +241,8 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
}
}
@Override
protected boolean shouldDragHandleBeVisible() {
boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
&& !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
return needsAllAppsEdu || super.shouldDragHandleBeVisible();
}
@Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
drawDragHandle(canvas);
}
private void drawBackground(Canvas canvas) {
if (mDrawingFlatColor) {
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
@ -311,9 +282,4 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mPaint.setColor(mShelfColor);
canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
}
@Override
public float getVisualTop() {
return mShelfTop;
}
}

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/drag_handle_indicator_no_shadow"
android:elevation="@dimen/vertical_drag_handle_elevation" />

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/vertical_drag_handle_width"
android:height="@dimen/vertical_drag_handle_height"
android:viewportWidth="18.0"
android:viewportHeight="6.0"
android:tint="?attr/workspaceTextColor" >
<path
android:pathData="M17,6c-0.15,0-0.3-0.03-0.45-0.11L9,2.12L1.45,5.89c-0.5,0.25-1.09,
0.05-1.34-0.45S0.06,4.35,0.55,4.11l8-4c0.28-0.14,0.61-0.14,0.89,0l8,4c0.49,0.25,0.69,
0.85,0.45,1.34C17.72,5.8,17.37,6,17,6z"
android:fillColor="@android:color/white" />
</vector>

View File

@ -28,6 +28,12 @@
android:clipToPadding="false"
android:importantForAccessibility="no">
<com.android.launcher3.views.AccessibilityActionsView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/home_screen"
/>
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace

View File

@ -40,14 +40,6 @@
<dimen name="workspace_page_indicator_line_height">1dp</dimen>
<dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
<!-- Hotseat/all-apps scrim -->
<dimen name="all_apps_scrim_blur">4dp</dimen>
<dimen name="vertical_drag_handle_width">18dp</dimen>
<dimen name="vertical_drag_handle_height">6dp</dimen>
<dimen name="vertical_drag_handle_elevation">1dp</dimen>
<dimen name="vertical_drag_handle_touch_size">48dp</dimen>
<dimen name="vertical_drag_handle_padding_in_vertical_bar_layout">16dp</dimen>
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">48dp</dimen>
<dimen name="drop_target_vertical_gap">20dp</dimen>

View File

@ -18,5 +18,4 @@
<drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
<drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
<drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
<drawable name="all_apps_arrow_shadow">@drawable/drag_handle_indicator_no_shadow</drawable>
</resources>

View File

@ -36,7 +36,7 @@
<!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
<string name="shortcut_not_available">Shortcut isn\'t available</string>
<!-- User visible name for the launcher/home screen. [CHAR_LIMIT=30] -->
<string name="home_screen">Home screen</string>
<string name="home_screen">Home</string>
<!-- Label for showing custom action list of a shortcut or widget. [CHAR_LIMIT=30] -->
<string name="custom_actions">Custom actions</string>
@ -88,10 +88,6 @@
<string name="all_apps_button_personal_label">Personal apps list</string>
<string name="all_apps_button_work_label">Work apps list</string>
<!-- Label for button in all applications label to go back home (to the workspace / desktop)
for accessibilty (spoken when the button gets focus). -->
<string name="all_apps_home_button_label">Home</string>
<!-- Label for remove drop target (from the homescreen only).
May appear next to uninstall_drop_target_label [CHAR_LIMIT=20] -->
<string name="remove_drop_target_label">Remove</string>

View File

@ -453,10 +453,10 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
} else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
mScrimView.setAlpha(alpha);
} else {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
mScrimView.setAlpha(1f);
}
}
});
@ -553,7 +553,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
} else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
mScrimView.setAlpha(alpha);
}
}
@ -1936,7 +1936,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
// Populate event with a fake title based on the current state.
// TODO: When can workspace be null?
text.add(mWorkspace == null
? getString(R.string.all_apps_home_button_label)
? getString(R.string.home_screen)
: mStateManager.getState().getDescription(this));
return result;
}

View File

@ -3263,7 +3263,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
if (nScreens == 0) {
// When the workspace is not loaded, we do not know how many screen will be bound.
return getContext().getString(R.string.all_apps_home_button_label);
return getContext().getString(R.string.home_screen);
}
return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
}

View File

@ -19,7 +19,6 @@ import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
@ -222,9 +221,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
(visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
// Set visibility of the container at the very beginning or end of the transition.
setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
hasAnyVisibleItem ? INSTANT : FINAL_FRAME);

View File

@ -36,7 +36,6 @@ public class OnboardingPrefs<T extends Launcher> {
public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
@ -56,7 +55,6 @@ public class OnboardingPrefs<T extends Launcher> {
@StringDef(value = {
HOME_BOUNCE_COUNT,
SHELF_BOUNCE_COUNT,
ALL_APPS_COUNT,
HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
@ -67,7 +65,6 @@ public class OnboardingPrefs<T extends Launcher> {
Map<String, Integer> maxCounts = new ArrayMap<>(4);
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.views.OptionsPopupView.OptionItem;
/**
* Placeholder view to expose additional Launcher actions via accessibility actions
*/
public class AccessibilityActionsView extends View implements StateListener<LauncherState> {
public AccessibilityActionsView(Context context) {
this(context, null);
}
public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
Launcher.getLauncher(context).getStateManager().addStateListener(this);
setWillNotDraw(true);
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
setImportantForAccessibility(finalState == NORMAL
? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo() {
AccessibilityNodeInfo info = super.createAccessibilityNodeInfo();
Launcher l = Launcher.getLauncher(getContext());
info.addAction(new AccessibilityAction(
R.string.all_apps_button_label, l.getText(R.string.all_apps_button_label)));
for (OptionItem item : OptionsPopupView.getOptions(l)) {
info.addAction(new AccessibilityAction(item.labelRes, l.getText(item.labelRes)));
}
return info;
}
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (super.performAccessibilityAction(action, arguments)) {
return true;
}
Launcher l = Launcher.getLauncher(getContext());
if (action == R.string.all_apps_button_label) {
l.getStateManager().goToState(ALL_APPS);
return true;
}
for (OptionItem item : OptionsPopupView.getOptions(l)) {
if (item.labelRes == action) {
if (item.eventId.getId() > 0) {
l.getStatsLogManager().logger().log(item.eventId);
}
if (item.clickListener.onLongClick(this)) {
return true;
}
}
}
return false;
}
}

View File

@ -85,10 +85,10 @@ public class OptionsPopupView extends ArrowPopup
if (item == null) {
return false;
}
if (item.mEventId.getId() > 0) {
mLauncher.getStatsLogManager().logger().log(item.mEventId);
if (item.eventId.getId() > 0) {
mLauncher.getStatsLogManager().logger().log(item.eventId);
}
if (item.mClickListener.onLongClick(view)) {
if (item.clickListener.onLongClick(view)) {
close(true);
return true;
}
@ -130,8 +130,8 @@ public class OptionsPopupView extends ArrowPopup
for (OptionItem item : items) {
DeepShortcutView view =
(DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
view.getIconView().setBackgroundResource(item.mIconRes);
view.getBubbleText().setText(item.mLabelRes);
view.getIconView().setBackgroundResource(item.iconRes);
view.getBubbleText().setText(item.labelRes);
view.setDividerVisibility(View.INVISIBLE);
view.setOnClickListener(popup);
view.setOnLongClickListener(popup);
@ -152,7 +152,13 @@ public class OptionsPopupView extends ArrowPopup
y = launcher.getDragLayer().getHeight() / 2;
}
RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
show(launcher, target, getOptions(launcher));
}
/**
* Returns the list of supported actions
*/
public static ArrayList<OptionItem> getOptions(Launcher launcher) {
ArrayList<OptionItem> options = new ArrayList<>();
int resString = Utilities.existsStyleWallpapers(launcher) ?
R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
@ -170,10 +176,10 @@ public class OptionsPopupView extends ArrowPopup
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
OptionsPopupView::startSettings));
show(launcher, target, options);
return options;
}
public static boolean onWidgetsClicked(View view) {
private static boolean onWidgetsClicked(View view) {
return openWidgets(Launcher.getLauncher(view.getContext())) != null;
}
@ -188,7 +194,7 @@ public class OptionsPopupView extends ArrowPopup
}
}
public static boolean startSettings(View view) {
private static boolean startSettings(View view) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings");
Launcher launcher = Launcher.getLauncher(view.getContext());
launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
@ -201,7 +207,7 @@ public class OptionsPopupView extends ArrowPopup
* Event handler for the wallpaper picker button that appears after a long press
* on the home screen.
*/
public static boolean startWallpaperPicker(View v) {
private static boolean startWallpaperPicker(View v) {
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!Utilities.isWallpaperAllowed(launcher)) {
Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
@ -233,17 +239,17 @@ public class OptionsPopupView extends ArrowPopup
public static class OptionItem {
private final int mLabelRes;
private final int mIconRes;
private final EventEnum mEventId;
private final OnLongClickListener mClickListener;
public final int labelRes;
public final int iconRes;
public final EventEnum eventId;
public final OnLongClickListener clickListener;
public OptionItem(int labelRes, int iconRes, EventEnum eventId,
OnLongClickListener clickListener) {
mLabelRes = labelRes;
mIconRes = iconRes;
mEventId = eventId;
mClickListener = clickListener;
this.labelRes = labelRes;
this.iconRes = iconRes;
this.eventId = eventId;
this.clickListener = clickListener;
}
}
}

View File

@ -15,119 +15,37 @@
*/
package com.android.launcher3.views;
import static android.content.Context.ACCESSIBILITY_SERVICE;
import static android.view.MotionEvent.ACTION_DOWN;
import static androidx.core.graphics.ColorUtils.compositeColors;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.RectEvaluator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.IntProperty;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import androidx.customview.widget.ExploreByTouchHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.List;
/**
* Simple scrim which draws a flat color
*/
public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
AccessibilityStateChangeListener {
public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
new IntProperty<ScrimView>("dragHandleAlpha") {
@Override
public Integer get(ScrimView scrimView) {
return scrimView.mDragHandleAlpha;
}
@Override
public void setValue(ScrimView scrimView, int value) {
scrimView.setDragHandleAlpha(value);
}
};
private static final int WALLPAPERS = R.string.wallpaper_button_text;
private static final int WIDGETS = R.string.widget_button_text;
private static final int SETTINGS = R.string.settings_button_text;
private static final int ALPHA_CHANNEL_COUNT = 1;
private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300;
// How much to delay before repeating the bounce.
private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200;
// Repeat this many times (i.e. total number of bounces is 1 + this).
private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2;
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener {
protected final T mLauncher;
private final WallpaperColorInfo mWallpaperColorInfo;
private final AccessibilityManager mAM;
protected final int mEndScrim;
protected final boolean mIsScrimDark;
private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
setImportantForAccessibility(finalState == ALL_APPS
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
};
protected float mMaxScrimAlpha;
protected float mProgress = 1;
@ -137,22 +55,6 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
protected int mEndFlatColor;
protected int mEndFlatColorAlpha;
protected final Point mDragHandleSize;
private final int mDragHandleTouchSize;
private final int mDragHandlePaddingInVerticalBarLayout;
protected float mDragHandleOffset;
private final Rect mDragHandleBounds;
private final RectF mHitRect = new RectF();
private ObjectAnimator mDragHandleAnim;
private final MultiValueAlpha mMultiValueAlpha;
private final AccessibilityHelper mAccessibilityHelper;
@Nullable
protected Drawable mDragHandle;
private int mDragHandleAlpha = 255;
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
mLauncher = Launcher.cast(Launcher.getLauncher(context));
@ -161,59 +63,24 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
mMaxScrimAlpha = 0.7f;
Resources res = context.getResources();
mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width),
res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height));
mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y);
mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size);
mDragHandlePaddingInVerticalBarLayout = context.getResources()
.getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout);
mAccessibilityHelper = createAccessibilityHelper();
ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
setFocusable(false);
mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
}
public AlphaProperty getAlphaProperty(int index) {
return mMultiValueAlpha.getProperty(index);
}
@NonNull
protected AccessibilityHelper createAccessibilityHelper() {
return new AccessibilityHelper();
}
@Override
public void setInsets(Rect insets) {
updateDragHandleBounds();
updateDragHandleVisibility();
}
public void setInsets(Rect insets) { }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateDragHandleBounds();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
mAM.addAccessibilityStateChangeListener(this);
onAccessibilityStateChanged(mAM.isEnabled());
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mWallpaperColorInfo.removeOnChangeListener(this);
mAM.removeAccessibilityStateChangeListener(this);
}
@Override
@ -234,10 +101,8 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
public void setProgress(float progress) {
if (mProgress != progress) {
mProgress = progress;
stopDragHandleEducationAnim();
updateColors();
updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
}
@ -260,286 +125,10 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
}
}
protected void updateDragHandleAlpha() {
if (mDragHandle != null) {
mDragHandle.setAlpha(mDragHandleAlpha);
}
}
private void setDragHandleAlpha(int alpha) {
if (alpha != mDragHandleAlpha) {
mDragHandleAlpha = alpha;
if (mDragHandle != null) {
mDragHandle.setAlpha(mDragHandleAlpha);
invalidate();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
}
drawDragHandle(canvas);
}
protected void drawDragHandle(Canvas canvas) {
if (mDragHandle != null) {
canvas.translate(0, -mDragHandleOffset);
mDragHandle.draw(canvas);
canvas.translate(0, mDragHandleOffset);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superHandledTouch = super.onTouchEvent(event);
if (event.getAction() == ACTION_DOWN) {
if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) {
if (startDragHandleEducationAnim()) {
return true;
}
}
stopDragHandleEducationAnim();
}
return superHandledTouch;
}
/**
* Animates the drag handle to demonstrate how to get to all apps.
* @return Whether the animation was started (false if drag handle is invisible).
*/
public boolean startDragHandleEducationAnim() {
stopDragHandleEducationAnim();
if (mDragHandle == null || mDragHandle.getAlpha() != 255) {
return false;
}
final Drawable drawable = mDragHandle;
mDragHandle = null;
Rect bounds = new Rect(mDragHandleBounds);
bounds.offset(0, -(int) mDragHandleOffset);
drawable.setBounds(bounds);
Rect topBounds = new Rect(bounds);
topBounds.offset(0, -bounds.height());
Rect invalidateRegion = new Rect(bounds);
invalidateRegion.top = topBounds.top;
final float progressToReachTop = 0.6f;
Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds);
frameTop.setInterpolator(DEACCEL);
Keyframe frameBot = Keyframe.ofObject(1, bounds);
frameBot.setInterpolator(ACCEL_DEACCEL);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds",
Keyframe.ofObject(0, bounds), frameTop, frameBot);
holder.setEvaluator(new RectEvaluator());
mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS;
// The bounce finishes by this progress, the rest of the duration just delays next bounce.
float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration;
mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion));
mDragHandleAnim.setDuration(totalBounceDuration);
mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress));
mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT);
getOverlay().add(drawable);
mDragHandleAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mDragHandleAnim = null;
getOverlay().remove(drawable);
updateDragHandleVisibility(drawable);
}
});
mDragHandleAnim.start();
return true;
}
private void stopDragHandleEducationAnim() {
if (mDragHandleAnim != null) {
mDragHandleAnim.end();
}
}
protected void updateDragHandleBounds() {
DeviceProfile grid = mLauncher.getDeviceProfile();
final int left;
final int width = getMeasuredWidth();
final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom;
final int topMargin;
if (grid.isVerticalBarLayout()) {
topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout;
if (grid.isSeascape()) {
left = width - grid.getInsets().right - mDragHandleSize.x
- mDragHandlePaddingInVerticalBarLayout;
} else {
left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout;
}
} else {
left = Math.round((width - mDragHandleSize.x) / 2f);
topMargin = grid.hotseatBarSizePx;
}
mDragHandleBounds.offsetTo(left, top - topMargin);
mHitRect.set(mDragHandleBounds);
// Inset outwards to increase touch size.
mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f,
(mDragHandleSize.y - mDragHandleTouchSize) / 2f);
if (mDragHandle != null) {
mDragHandle.setBounds(mDragHandleBounds);
}
}
@Override
public void onAccessibilityStateChanged(boolean enabled) {
StateManager<LauncherState> stateManager = mLauncher.getStateManager();
stateManager.removeStateListener(mAccessibilityLauncherStateListener);
if (enabled) {
stateManager.addStateListener(mAccessibilityLauncherStateListener);
mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState());
} else {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
updateDragHandleVisibility();
}
public void updateDragHandleVisibility() {
updateDragHandleVisibility(null);
}
private void updateDragHandleVisibility(@Nullable Drawable recycle) {
boolean visible = shouldDragHandleBeVisible();
boolean wasVisible = mDragHandle != null;
if (visible != wasVisible) {
if (visible) {
mDragHandle = recycle != null ? recycle :
mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow);
mDragHandle.setBounds(mDragHandleBounds);
updateDragHandleAlpha();
} else {
mDragHandle = null;
}
invalidate();
}
}
protected boolean shouldDragHandleBeVisible() {
return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
}
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
}
@Override
public void onFocusChanged(boolean gainFocus, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
protected class AccessibilityHelper extends ExploreByTouchHelper {
private static final int DRAG_HANDLE_ID = 1;
public AccessibilityHelper() {
super(ScrimView.this);
}
@Override
protected int getVirtualViewAt(float x, float y) {
return mHitRect.contains((int) x, (int) y)
? DRAG_HANDLE_ID : INVALID_ID;
}
@Override
protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
virtualViewIds.add(DRAG_HANDLE_ID);
}
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId,
AccessibilityNodeInfoCompat node) {
node.setContentDescription(getContext().getString(R.string.all_apps_button_label));
node.setBoundsInParent(mDragHandleBounds);
getLocationOnScreen(mTempPos);
mTempRect.set(mDragHandleBounds);
mTempRect.offset(mTempPos[0], mTempPos[1]);
node.setBoundsInScreen(mTempRect);
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
node.setClickable(true);
node.setFocusable(true);
if (mLauncher.isInState(NORMAL)) {
Context context = getContext();
if (Utilities.isWallpaperAllowed(context)) {
node.addAction(
new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS)));
}
node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS)));
node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS)));
}
}
@Override
protected boolean onPerformActionForVirtualView(
int virtualViewId, int action, Bundle arguments) {
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
mLauncher.getUserEventDispatcher().logActionOnControl(
Action.Touch.TAP, ControlType.ALL_APPS_BUTTON,
mLauncher.getStateManager().getState().containerType);
mLauncher.getStateManager().goToState(ALL_APPS);
return true;
} else if (action == WALLPAPERS) {
return OptionsPopupView.startWallpaperPicker(ScrimView.this);
} else if (action == WIDGETS) {
int originalImportanceForAccessibility = getImportantForAccessibility();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
if (widgetsFullSheet == null) {
setImportantForAccessibility(originalImportanceForAccessibility);
return false;
}
widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {}
@Override
public void onViewDetachedFromWindow(View view) {
setImportantForAccessibility(originalImportanceForAccessibility);
widgetsFullSheet.removeOnAttachStateChangeListener(this);
}
});
return true;
} else if (action == SETTINGS) {
return OptionsPopupView.startSettings(ScrimView.this);
}
return false;
}
}
/**
* @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top.
*/
public float getVisualTop() {
return Float.MAX_VALUE;
}
}