Merge "Removing some unused code:" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
deleted file mode 100644
index d33859b..0000000
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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.statehandlers;
-
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.util.NavigationMode.TWO_BUTTONS;
-import static com.android.quickstep.AnimatedFloat.VALUE;
-
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * State handler for animating back button alpha in two-button nav mode.
- */
-public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
-
- private final QuickstepLauncher mLauncher;
- private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
-
- public BackButtonAlphaHandler(QuickstepLauncher launcher) {
- mLauncher = launcher;
- }
-
- @Override
- public void setState(LauncherState state) { }
-
- @Override
- public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
- PendingAnimation animation) {
- if (DisplayController.getNavigationMode(mLauncher) != TWO_BUTTONS) {
- return;
- }
-
- mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastNavButtonAlpha();
- animation.setFloat(mBackAlpha, VALUE,
- mLauncher.shouldBackButtonBeHidden(toState) ? 0 : 1, LINEAR);
- }
-
- private void updateBackAlpha() {
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
- QuickstepLauncher.SET_BACK_BUTTON_ALPHA, mBackAlpha.value, false /* animate */);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 0a99204..585dec5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,14 +17,11 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -47,7 +44,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.NavigationMode.TWO_BUTTONS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import android.animation.AnimatorSet;
@@ -72,7 +68,6 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -96,7 +91,6 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -171,12 +165,6 @@
*/
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
- /**
- * Reusable command for applying the back button alpha on the background thread.
- */
- public static final AsyncCommand SET_BACK_BUTTON_ALPHA =
- (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setNavBarButtonAlpha(
- Float.intBitsToFloat(arg1), arg2 != 0);
private FixedContainerItems mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
@@ -299,11 +287,6 @@
@Override
protected void onActivityFlagsChanged(int changeBits) {
- if ((changeBits
- & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
- onLauncherStateOrFocusChanged();
- }
-
if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
mDepthController.setActivityStarted(isStarted());
}
@@ -729,7 +712,6 @@
super.collectStateHandlers(out);
out.add(getDepthController());
out.add(new RecentsViewStateController(this));
- out.add(new BackButtonAlphaHandler(this));
}
public DepthController getDepthController() {
@@ -788,39 +770,6 @@
}
@Override
- public void onDragLayerHierarchyChanged() {
- onLauncherStateOrFocusChanged();
- }
-
- public boolean shouldBackButtonBeHidden(LauncherState toState) {
- NavigationMode mode = DisplayController.getNavigationMode(this);
- boolean shouldBackButtonBeHidden = mode.hasGestures
- && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
- && hasWindowFocus()
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
- if (shouldBackButtonBeHidden) {
- // Show the back button if there is a floating view visible.
- shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
- TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
- }
- return shouldBackButtonBeHidden;
- }
-
- /**
- * Sets the back button visibility based on the current state/window focus.
- */
- private void onLauncherStateOrFocusChanged() {
- boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
- if (DisplayController.getNavigationMode(this) == TWO_BUTTONS) {
- UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
- shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
- }
- if (getDragLayer() != null) {
- getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
- }
- }
-
- @Override
public void finishBindingItems(IntSet pagesBoundFirst) {
super.finishBindingItems(pagesBoundFirst);
// Instantiate and initialize WellbeingModel now that its loading won't interfere with
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 1e7e89e..e84eb56 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -144,7 +144,7 @@
RunnableList callbackList = null;
if (taskView != null) {
taskView.setEndQuickswitchCuj(true);
- callbackList = taskView.launchTaskAnimated();
+ callbackList = taskView.launchTasks();
}
if (callbackList != null) {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 8e9ff2f..b57f2ce 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,7 +17,6 @@
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.ActivityManager;
@@ -43,8 +42,6 @@
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -72,7 +69,7 @@
/**
* Holds the reference to SystemUI.
*/
-public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayInfoChangeListener {
+public class SystemUiProxy implements ISystemUiProxy {
private static final String TAG = SystemUiProxy.class.getSimpleName();
public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
@@ -106,29 +103,16 @@
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
private boolean mLastShelfVisible;
- private float mLastNavButtonAlpha;
- private boolean mLastNavButtonAnimate;
- private boolean mHasNavButtonAlphaBeenSet = false;
- private Runnable mPendingSetNavButtonAlpha = null;
private Context mContext;
// TODO(141886704): Find a way to remove this
private int mLastSystemUiStateFlags;
public SystemUiProxy(Context context) {
- DisplayController.INSTANCE.get(context).addChangeListener(this);
mContext = context;
}
@Override
- public void onDisplayInfoChanged(Context context, Info info, int flags) {
- if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- // Whenever the nav mode changes, force reset the nav button alpha
- setNavBarButtonAlpha(1f, false);
- }
- }
-
- @Override
public void onBackPressed() {
if (mSystemUiProxy != null) {
try {
@@ -205,11 +189,6 @@
if (mBackAnimation != null && mBackToLauncherCallback != null) {
setBackToLauncherCallback(mBackToLauncherCallback);
}
-
- if (mPendingSetNavButtonAlpha != null) {
- mPendingSetNavButtonAlpha.run();
- mPendingSetNavButtonAlpha = null;
- }
}
public void clearProxy() {
@@ -272,31 +251,6 @@
}
}
- public float getLastNavButtonAlpha() {
- return mLastNavButtonAlpha;
- }
-
- @Override
- public void setNavBarButtonAlpha(float alpha, boolean animate) {
- boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
- || animate != mLastNavButtonAnimate
- || !mHasNavButtonAlphaBeenSet;
- if (changed) {
- if (mSystemUiProxy == null) {
- mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
- } else {
- mLastNavButtonAlpha = alpha;
- mLastNavButtonAnimate = animate;
- mHasNavButtonAlphaBeenSet = true;
- try {
- mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
- }
- }
- }
- }
-
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (mSystemUiProxy != null) {
@@ -895,8 +849,7 @@
if (mRecentTasks != null
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
try {
- return new ArrayList<ActivityManager.RunningTaskInfo>(
- Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
+ return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
} catch (RemoteException e) {
Log.w(TAG, "Failed call getRunningTasks", e);
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 0bed51d..3920c56 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.AbstractFloatingView.getAnyView;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -13,6 +14,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -162,6 +164,21 @@
}
}
+ @Override
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
+ if (iconView == mIconView2 && showedTaskMenu && !isGridTask()) {
+ // Adjust the position of the secondary task's menu view (only on phones)
+ TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ getRecentsView().getPagedOrientationHandler()
+ .setSecondaryTaskMenuPosition(mSplitBoundsConfig, this,
+ deviceProfile, mTaskIdAttributeContainer[0].getThumbnailView(),
+ taskMenuView);
+ }
+ return showedTaskMenu;
+ }
+
@Nullable
@Override
public RunnableList launchTaskAnimated() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index c1711d1..6815745 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,8 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -156,23 +154,6 @@
mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
setY(pagedOrientationHandler.getTaskMenuY(
adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
-
- // TODO(b/193432925) temporary menu placement for split screen task menus
- TaskIdAttributeContainer[] taskIdAttributeContainers =
- mTaskView.getTaskIdAttributeContainers();
- if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
- if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
- return;
- }
- Rect r = new Rect();
- mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
- if (deviceProfile.isLandscape) {
- setX(r.left);
- } else {
- setY(r.top);
-
- }
- }
}
public void onRotationChanged() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index a84ea63..26bae35 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -689,12 +689,13 @@
/**
* Launch of the current task (both live and inactive tasks) with an animation.
*/
- public void launchTasks() {
+ public RunnableList launchTasks() {
RecentsView recentsView = getRecentsView();
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+ RunnableList runnableList = new RunnableList();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
- return;
+ return runnableList;
}
mIsClickableAsLiveTile = false;
@@ -721,7 +722,7 @@
// here, try to launch the task as a non live tile task.
launchTaskAnimated();
mIsClickableAsLiveTile = true;
- return;
+ return runnableList;
}
AnimatorSet anim = new AnimatorSet();
@@ -746,13 +747,24 @@
launchTaskAnimated();
}
mIsClickableAsLiveTile = true;
+ runEndCallback();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ runEndCallback();
+ }
+
+ private void runEndCallback() {
+ runnableList.executeAllAndDestroy();
}
});
anim.start();
recentsView.onTaskLaunchedInLiveTileMode();
} else {
- launchTaskAnimated();
+ return launchTaskAnimated();
}
+ return runnableList;
}
/**
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index 11143fb..d15b906 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -20,6 +20,8 @@
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_pill_height"
android:layout_gravity="center_horizontal"
+ android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
+ android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
android:orientation="horizontal"
style="@style/TextHeadline">
@@ -28,7 +30,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/all_apps_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_personal_tab"
@@ -41,7 +42,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/all_apps_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_work_tab"
diff --git a/res/layout/search_results_rv_layout.xml b/res/layout/search_results_rv_layout.xml
index 567cb5f..9127521 100644
--- a/res/layout/search_results_rv_layout.xml
+++ b/res/layout/search_results_rv_layout.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_results_list_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:focusable="true" />
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index d2fa5fa..81b28ba 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -26,6 +26,7 @@
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp"
android:background="@drawable/work_apps_toggle_background"
+ android:forceHasOverlappingRendering="false"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_corp_off"
android:layout_marginBottom="@dimen/work_fab_margin_bottom"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 21dbc5f..5ee6fce 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -109,10 +109,6 @@
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
| TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG;
- // Usually we show the back button when a floating view is open. Instead, hide for these types.
- public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
- | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
-
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
& ~TYPE_ALL_APPS_EDU;
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 4629ca7..11f2020 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
import android.content.Context;
import android.text.TextUtils;
@@ -33,8 +33,6 @@
* Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion
*/
public class ExtendedEditText extends EditText {
-
- private boolean mShowImeAfterFirstLayout;
private boolean mForceDisableSuggestions = false;
/**
@@ -85,28 +83,21 @@
return false;
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mShowImeAfterFirstLayout) {
- // soft input only shows one frame after the layout of the EditText happens,
- post(() -> {
- showSoftInput();
- mShowImeAfterFirstLayout = false;
- });
- }
- }
-
-
public void showKeyboard() {
- mShowImeAfterFirstLayout = !showSoftInput();
+ onKeyboardShown();
+ showSoftInput();
}
public void hideKeyboard() {
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
clearFocus();
}
+ protected void onKeyboardShown() {
+ ActivityContext.lookupContext(getContext()).getStatsLogManager()
+ .keyboardStateManager().setKeyboardState(SHOW);
+ }
+
private boolean showSoftInput() {
return requestFocus() &&
getContext().getSystemService(InputMethodManager.class)
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4312c3d..6bdfa1c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -194,7 +194,6 @@
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.FloatingIconView;
@@ -1123,6 +1122,7 @@
.log(getAllAppsEntryEvent().get());
}
}
+ updateDisallowBack();
}
/**
@@ -1653,16 +1653,6 @@
}
}
- /**
- * Hides the keyboard if visible
- */
- public void hideKeyboard() {
- final View v = getWindow().peekDecorView();
- if (v != null && v.getWindowToken() != null) {
- UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
- }
- }
-
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
@@ -3133,7 +3123,18 @@
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
- public void onDragLayerHierarchyChanged() { }
+ public void onDragLayerHierarchyChanged() {
+ updateDisallowBack();
+ }
+
+ private void updateDisallowBack() {
+ LauncherRootView rv = getRootView();
+ if (rv != null) {
+ boolean disableBack = getStateManager().getState() == NORMAL
+ && AbstractFloatingView.getTopOpenView(this) == null;
+ rv.setDisallowBackGesture(disableBack);
+ }
+ }
@Override
public void returnToHomescreen() {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index c3b5392..31a7d18 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -71,17 +71,14 @@
public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
// Flag to indicate that workspace should draw page background
public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
- // True if the back button should be hidden when in this state (assuming no floating views are
- // open, launcher has window focus, etc).
- public static final int FLAG_HIDE_BACK_BUTTON = BaseState.getFlag(4);
// Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
- public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(5);
+ public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
// Flag to inticate that all popups should be closed when this state is enabled.
- public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
- public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+ public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
+ public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
// Flag indicating that hotseat and its contents are not accessible.
- public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(8);
+ public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
public static final float NO_OFFSET = 0;
@@ -110,8 +107,7 @@
*/
public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
LAUNCHER_STATE_HOME,
- FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
- FLAG_HAS_SYS_UI_SCRIM) {
+ FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HAS_SYS_UI_SCRIM) {
@Override
public int getTransitionDuration(Context context, boolean isToState) {
// Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index e33e44e..aa9cfd1 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -34,7 +36,6 @@
import com.android.launcher3.views.AppLauncher;
import java.util.ArrayList;
-import java.util.Objects;
/**
* All apps container view with search support for use in a dragging activity.
@@ -44,6 +45,11 @@
public class ActivityAllAppsContainerView<T extends Context & AppLauncher
& DeviceProfileListenable> extends BaseAllAppsContainerView<T> {
+ private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
+
+ // Used to animate Search results out and A-Z apps in, or vice-versa.
+ private final SearchTransitionController mSearchTransitionController;
+
protected SearchUiManager mSearchUiManager;
/**
* View that defines the search box. Result is rendered inside the recycler view defined in the
@@ -52,6 +58,7 @@
private View mSearchContainer;
/** {@code true} when rendered view is in search state instead of the scroll state. */
private boolean mIsSearching;
+ private boolean mRebindAdaptersAfterSearchAnimation;
public ActivityAllAppsContainerView(Context context) {
this(context, null);
@@ -63,6 +70,8 @@
public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+
+ mSearchTransitionController = new SearchTransitionController(this);
}
public SearchUiManager getSearchUiManager() {
@@ -73,19 +82,10 @@
return mSearchContainer;
}
- /** Updates all apps container with the latest search query. */
- public void setLastSearchQuery(String query) {
- mIsSearching = true;
- rebindAdapters();
- mHeader.setCollapsed(true);
- }
-
/** Invoke when the current search session is finished. */
public void onClearSearchResult() {
- mIsSearching = false;
- mHeader.setCollapsed(false);
+ animateToSearchState(false);
rebindAdapters();
- mHeader.reset(false);
}
/**
@@ -93,12 +93,42 @@
*/
public void setSearchResults(ArrayList<AdapterItem> results) {
if (getSearchResultList().setSearchResults(results)) {
- for (int i = 0; i < mAH.size(); i++) {
- if (mAH.get(i).mRecyclerView != null) {
- mAH.get(i).mRecyclerView.onSearchResultsChanged();
- }
- }
+ getSearchRecyclerView().onSearchResultsChanged();
}
+ if (results != null) {
+ animateToSearchState(true);
+ }
+ }
+
+ private void animateToSearchState(boolean goingToSearch) {
+ animateToSearchState(goingToSearch, DEFAULT_SEARCH_TRANSITION_DURATION_MS);
+ }
+
+ private void animateToSearchState(boolean goingToSearch, long durationMs) {
+ if (!mSearchTransitionController.isRunning() && goingToSearch == isSearching()) {
+ return;
+ }
+ if (goingToSearch) {
+ // Fade out the button to pause work apps.
+ mWorkManager.onActivePageChanged(SEARCH);
+ }
+ mSearchTransitionController.animateToSearchState(goingToSearch, durationMs,
+ /* onEndRunnable = */ () -> {
+ mIsSearching = goingToSearch;
+ updateSearchResultsVisibility();
+ int previousPage = getCurrentPage();
+ if (mRebindAdaptersAfterSearchAnimation) {
+ rebindAdapters(false);
+ mRebindAdaptersAfterSearchAnimation = false;
+ }
+ if (!goingToSearch) {
+ setSearchResults(null);
+ if (mViewPager != null) {
+ mViewPager.setCurrentPage(previousPage);
+ }
+ onActivePageChanged(previousPage);
+ }
+ });
}
@Override
@@ -121,6 +151,8 @@
super.reset(animate);
// Reset the search bar after transitioning home.
mSearchUiManager.resetSearch();
+ // Animate to A-Z with 0 time to reset the animation with proper state management.
+ animateToSearchState(false, 0);
}
@Override
@@ -157,21 +189,30 @@
}
@Override
+ public void onActivePageChanged(int currentActivePage) {
+ if (mSearchTransitionController.isRunning()) {
+ // Will be called at the end of the animation.
+ return;
+ }
+ super.onActivePageChanged(currentActivePage);
+ }
+
+ @Override
protected void rebindAdapters(boolean force) {
+ if (mSearchTransitionController.isRunning()) {
+ mRebindAdaptersAfterSearchAnimation = true;
+ return;
+ }
super.rebindAdapters(force);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
- || getMainAdapterProvider().getDecorator() == null) {
+ || getMainAdapterProvider().getDecorator() == null
+ || getSearchRecyclerView() == null) {
return;
}
RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
- mAH.stream()
- .map(adapterHolder -> adapterHolder.mRecyclerView)
- .filter(Objects::nonNull)
- .forEach(v -> {
- v.removeItemDecoration(decoration); // Remove in case it is already added.
- v.addItemDecoration(decoration);
- });
+ getSearchRecyclerView().removeItemDecoration(decoration); // In case it is already added.
+ getSearchRecyclerView().addItemDecoration(decoration);
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 31e3890..21a7dfb 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
import android.content.Context;
import android.graphics.Canvas;
@@ -162,8 +161,7 @@
requestFocus();
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
break;
case SCROLL_STATE_IDLE:
mgr.logger().sendToInteractionJankMonitor(
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 6990e57..42f8b0c 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -127,6 +127,11 @@
public boolean isContentSame(AdapterItem other) {
return itemInfo == null && other.itemInfo == null;
}
+
+ /** Sets the alpha of the decorator for this item. Returns true if successful. */
+ public boolean setDecorationFillAlpha(int alpha) {
+ return false;
+ }
}
protected final T mActivityContext;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f3c5dd6..879a159 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -107,12 +106,13 @@
updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
}
};
- private final WorkProfileManager mWorkManager;
+
+ protected final WorkProfileManager mWorkManager;
private final Paint mNavBarScrimPaint;
private int mNavBarScrimHeight = 0;
- private AllAppsPagedView mViewPager;
+ protected AllAppsPagedView mViewPager;
private SearchRecyclerView mSearchRecyclerView;
protected FloatingHeaderView mHeader;
@@ -349,7 +349,7 @@
* The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
* hidden while searching.
**/
- private View getAppsRecyclerViewContainer() {
+ protected View getAppsRecyclerViewContainer() {
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
@@ -503,8 +503,7 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
}
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ mActivityContext.hideKeyboard();
});
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> {
@@ -512,8 +511,7 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
}
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ mActivityContext.hideKeyboard();
});
setDeviceManagementResources();
onActivePageChanged(mViewPager.getNextPage());
@@ -527,7 +525,7 @@
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
}
- private void updateSearchResultsVisibility() {
+ protected void updateSearchResultsVisibility() {
if (isSearching()) {
getSearchRecyclerView().setVisibility(VISIBLE);
getAppsRecyclerViewContainer().setVisibility(GONE);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index c5bdb69..8ec2aeb 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -42,6 +42,7 @@
import com.android.systemui.plugins.PluginListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Map;
public class FloatingHeaderView extends LinearLayout implements
@@ -82,8 +83,8 @@
protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
// These two values are necessary to ensure that the header protection is drawn correctly.
- private final int mHeaderTopAdjustment;
- private final int mHeaderBottomAdjustment;
+ private final int mTabsAdditionalPaddingTop;
+ private final int mTabsAdditionalPaddingBottom;
private boolean mHeaderProtectionSupported;
protected ViewGroup mTabLayout;
@@ -91,7 +92,6 @@
private AllAppsRecyclerView mWorkRV;
private SearchRecyclerView mSearchRV;
private AllAppsRecyclerView mCurrentRV;
- public boolean mHeaderCollapsed;
protected int mSnappedScrolledY;
private int mTranslationY;
@@ -100,7 +100,12 @@
protected boolean mTabsHidden;
protected int mMaxTranslation;
- private boolean mCollapsed = false;
+ // Whether the header has been scrolled off-screen.
+ private boolean mHeaderCollapsed;
+ // Whether floating rows like predicted apps are hidden.
+ private boolean mFloatingRowsCollapsed;
+ // Total height of all current floating rows. Collapsed rows == 0 height.
+ private int mFloatingRowsHeight;
// This is initialized once during inflation and stays constant after that. Fixed views
// cannot be added or removed dynamically.
@@ -117,9 +122,9 @@
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mHeaderTopAdjustment = context.getResources()
+ mTabsAdditionalPaddingTop = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_top_adjustment);
- mHeaderBottomAdjustment = context.getResources()
+ mTabsAdditionalPaddingBottom = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_bottom_adjustment);
mHeaderProtectionSupported = context.getResources().getBoolean(
R.bool.config_header_protection_supported);
@@ -148,6 +153,7 @@
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
mAllRows = mFixedRows;
+ updateFloatingRowsHeight();
}
@Override
@@ -179,6 +185,7 @@
count++;
}
}
+ updateFloatingRowsHeight();
}
@Override
@@ -195,7 +202,7 @@
int oldMaxHeight = mMaxTranslation;
updateExpectedHeight();
- if (mMaxTranslation != oldMaxHeight || mCollapsed) {
+ if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) {
BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent();
if (parent != null) {
parent.setupHeader();
@@ -258,20 +265,19 @@
}
private void updateExpectedHeight() {
+ updateFloatingRowsHeight();
mMaxTranslation = 0;
- if (mCollapsed) {
+ if (mFloatingRowsCollapsed) {
return;
}
- for (FloatingHeaderRow row : mAllRows) {
- mMaxTranslation += row.getExpectedHeight();
- }
+ mMaxTranslation += mFloatingRowsHeight;
if (!mTabsHidden) {
- mMaxTranslation += mHeaderBottomAdjustment;
+ mMaxTranslation += mTabsAdditionalPaddingBottom;
}
}
public int getMaxTranslation() {
- if (mMaxTranslation == 0 && mTabsHidden) {
+ if (mMaxTranslation == 0 && (mTabsHidden || mFloatingRowsCollapsed)) {
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
} else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop();
@@ -312,7 +318,7 @@
int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
- if (mCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
+ if (mFloatingRowsCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
// we hide it completely if already capped (for opening search anim)
for (FloatingHeaderRow row : mAllRows) {
row.setVerticalScroll(0, true /* isScrolledOut */);
@@ -325,11 +331,11 @@
mTabLayout.setTranslationY(mTranslationY);
- int clipTop = getPaddingTop() - mHeaderTopAdjustment;
+ int clipTop = getPaddingTop() - mTabsAdditionalPaddingTop;
if (mTabsHidden) {
- clipTop += getPaddingBottom() - mHeaderBottomAdjustment;
+ clipTop += getPaddingBottom() - mTabsAdditionalPaddingBottom;
}
- mRVClip.top = mTabsHidden ? clipTop : 0;
+ mRVClip.top = mTabsHidden || mFloatingRowsCollapsed ? clipTop : 0;
mHeaderClip.top = clipTop;
// clipping on a draw might cause additional redraw
setClipBounds(mHeaderClip);
@@ -347,10 +353,12 @@
/**
* Hides all the floating rows
*/
- public void setCollapsed(boolean collapse) {
- if (mCollapsed == collapse) return;
+ public void setFloatingRowsCollapsed(boolean collapsed) {
+ if (mFloatingRowsCollapsed == collapsed) {
+ return;
+ }
- mCollapsed = collapse;
+ mFloatingRowsCollapsed = collapsed;
onHeightUpdated();
}
@@ -376,6 +384,30 @@
return !mHeaderCollapsed;
}
+ /** Returns true if personal/work tabs are currently in use. */
+ public boolean usingTabs() {
+ return !mTabsHidden;
+ }
+
+ ViewGroup getTabLayout() {
+ return mTabLayout;
+ }
+
+ /** Calculates the combined height of any floating rows (e.g. predicted apps, app divider). */
+ private void updateFloatingRowsHeight() {
+ mFloatingRowsHeight =
+ Arrays.stream(mAllRows).mapToInt(FloatingHeaderRow::getExpectedHeight).sum();
+ }
+
+ /** Gets the combined height of any floating rows (e.g. predicted apps, app divider). */
+ int getFloatingRowsHeight() {
+ return mFloatingRowsHeight;
+ }
+
+ int getTabsAdditionalPaddingBottom() {
+ return mTabsAdditionalPaddingBottom;
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslationY = (Integer) animation.getAnimatedValue();
@@ -447,9 +479,10 @@
// we only want to show protection when work tab is available and header is either
// collapsed or animating to/from collapsed state
- if (mTabsHidden || !mHeaderCollapsed) {
+ if (mTabsHidden || mFloatingRowsCollapsed || !mHeaderCollapsed) {
return 0;
}
- return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
+ return Math.max(0,
+ getTabLayout().getBottom() - getPaddingTop() + getPaddingBottom() + mTranslationY);
}
}
diff --git a/src/com/android/launcher3/allapps/SearchRecyclerView.java b/src/com/android/launcher3/allapps/SearchRecyclerView.java
index 482bd29..9d1dfc0 100644
--- a/src/com/android/launcher3/allapps/SearchRecyclerView.java
+++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java
@@ -17,12 +17,17 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
import com.android.launcher3.views.RecyclerViewFastScroller;
/** A RecyclerView for AllApps Search results. */
public class SearchRecyclerView extends AllAppsRecyclerView {
- private static final String TAG = "SearchRecyclerView";
+
+ private Consumer<View> mChildAttachedConsumer;
public SearchRecyclerView(Context context) {
this(context, null);
@@ -41,6 +46,11 @@
super(context, attrs, defStyleAttr, defStyleRes);
}
+ /** This will be called just before a new child is attached to the window. */
+ public void setChildAttachedConsumer(Consumer<View> childAttachedConsumer) {
+ mChildAttachedConsumer = childAttachedConsumer;
+ }
+
@Override
protected void updatePoolSize() {
RecycledViewPool pool = getRecycledViewPool();
@@ -57,4 +67,12 @@
public RecyclerViewFastScroller getScrollbar() {
return null;
}
+
+ @Override
+ public void onChildAttachedToWindow(@NonNull View child) {
+ if (mChildAttachedConsumer != null) {
+ mChildAttachedConsumer.accept(child);
+ }
+ super.onChildAttachedToWindow(child);
+ }
}
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
new file mode 100644
index 0000000..03529af
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2022 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.allapps;
+
+import static android.view.View.VISIBLE;
+
+import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+
+/** Coordinates the transition between Search and A-Z in All Apps. */
+public class SearchTransitionController {
+
+ // Interpolator when the user taps the QSB while already in All Apps.
+ private static final Interpolator DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7;
+ // Interpolator when the user taps the QSB from home screen, so transition to all apps is
+ // happening simultaneously.
+ private static final Interpolator DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = LINEAR;
+
+ /**
+ * These values represent points on the [0, 1] animation progress spectrum. They are used to
+ * animate items in the {@link SearchRecyclerView}.
+ */
+ private static final float TOP_CONTENT_FADE_PROGRESS_START = 0.133f;
+ private static final float CONTENT_FADE_PROGRESS_DURATION = 0.083f;
+ private static final float TOP_BACKGROUND_FADE_PROGRESS_START = 0.633f;
+ private static final float BACKGROUND_FADE_PROGRESS_DURATION = 0.15f;
+ private static final float CONTENT_STAGGER = 0.01f; // Progress before next item starts fading.
+
+ private static final FloatProperty<SearchTransitionController> SEARCH_TO_AZ_PROGRESS =
+ new FloatProperty<SearchTransitionController>("searchToAzProgress") {
+ @Override
+ public Float get(SearchTransitionController controller) {
+ return controller.getSearchToAzProgress();
+ }
+
+ @Override
+ public void setValue(SearchTransitionController controller, float progress) {
+ controller.setSearchToAzProgress(progress);
+ }
+ };
+
+ private final ActivityAllAppsContainerView<?> mAllAppsContainerView;
+
+ private ObjectAnimator mSearchToAzAnimator = null;
+ private float mSearchToAzProgress = 1f;
+
+ public SearchTransitionController(ActivityAllAppsContainerView<?> allAppsContainerView) {
+ mAllAppsContainerView = allAppsContainerView;
+ }
+
+ /** Returns true if a transition animation is currently in progress. */
+ public boolean isRunning() {
+ return mSearchToAzAnimator != null;
+ }
+
+ /**
+ * Starts the transition to or from search state. If a transition is already in progress, the
+ * animation will start from that point with the new duration, and the previous onEndRunnable
+ * will not be called.
+ *
+ * @param goingToSearch true if will be showing search results, otherwise will be showing a-z
+ * @param duration time in ms for the animation to run
+ * @param onEndRunnable will be called when the animation finishes, unless another animation is
+ * scheduled in the meantime
+ */
+ public void animateToSearchState(boolean goingToSearch, long duration, Runnable onEndRunnable) {
+ float targetProgress = goingToSearch ? 0 : 1;
+
+ if (mSearchToAzAnimator != null) {
+ mSearchToAzAnimator.cancel();
+ }
+
+ mSearchToAzAnimator = ObjectAnimator.ofFloat(this, SEARCH_TO_AZ_PROGRESS, targetProgress);
+ boolean inAllApps = Launcher.getLauncher(
+ mAllAppsContainerView.getContext()).getStateManager().isInStableState(
+ LauncherState.ALL_APPS);
+ mSearchToAzAnimator.setDuration(duration).setInterpolator(
+ inAllApps ? DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS
+ : DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS);
+ mSearchToAzAnimator.addListener(forEndCallback(() -> mSearchToAzAnimator = null));
+ if (!goingToSearch) {
+ mSearchToAzAnimator.addListener(forSuccessCallback(() -> {
+ mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(false);
+ mAllAppsContainerView.getFloatingHeaderView().reset(false /* animate */);
+ mAllAppsContainerView.getAppsRecyclerViewContainer().setTranslationY(0);
+ }));
+ }
+ mSearchToAzAnimator.addListener(forSuccessCallback(onEndRunnable));
+
+ mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true);
+ mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+ getSearchRecyclerView().setVisibility(VISIBLE);
+ getSearchRecyclerView().setChildAttachedConsumer(this::onSearchChildAttached);
+ mSearchToAzAnimator.start();
+ }
+
+ private SearchRecyclerView getSearchRecyclerView() {
+ return mAllAppsContainerView.getSearchRecyclerView();
+ }
+
+ private void setSearchToAzProgress(float searchToAzProgress) {
+ mSearchToAzProgress = searchToAzProgress;
+ int searchHeight = updateSearchRecyclerViewProgress();
+
+ FloatingHeaderView headerView = mAllAppsContainerView.getFloatingHeaderView();
+
+ // Add predictions + app divider height to account for predicted apps which will now be in
+ // the Search RV instead of the floating header view. Note `getFloatingRowsHeight` returns 0
+ // when predictions are not shown.
+ int appsTranslationY = searchHeight + headerView.getFloatingRowsHeight();
+
+ if (headerView.usingTabs()) {
+ // Move tabs below the search results, and fade them out in 20% of the animation.
+ headerView.setTranslationY(searchHeight);
+ headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+
+ // Account for the additional padding added for the tabs.
+ appsTranslationY -=
+ headerView.getPaddingTop() - headerView.getTabsAdditionalPaddingBottom();
+ }
+
+ View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
+ appsContainer.setTranslationY(appsTranslationY);
+ // Fade apps out with tabs (in 20% of the total animation).
+ appsContainer.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+ }
+
+ /**
+ * Updates the children views of SearchRecyclerView based on the current animation progress.
+ *
+ * @return the total height of animating views (excluding any app icons).
+ */
+ private int updateSearchRecyclerViewProgress() {
+ int numSearchResultsAnimated = 0;
+ int totalHeight = 0;
+ int appRowHeight = 0;
+ Integer top = null;
+ SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
+ for (int i = 0; i < searchRecyclerView.getChildCount(); i++) {
+ View searchResultView = searchRecyclerView.getChildAt(i);
+ if (searchResultView == null) {
+ continue;
+ }
+
+ if (top == null) {
+ top = searchResultView.getTop();
+ }
+
+ if (searchResultView instanceof BubbleTextView) {
+ // The first app icon will set appRowHeight, which will also contribute to
+ // totalHeight. Additional app icons should remove the appRowHeight to remain in
+ // the same row as the first app.
+ searchResultView.setY(top + totalHeight - appRowHeight);
+ if (appRowHeight == 0) {
+ appRowHeight = searchResultView.getHeight();
+ totalHeight += appRowHeight;
+ }
+ // Don't scale/fade app row.
+ continue;
+ }
+
+ // Adjust content alpha based on start progress and stagger.
+ float startContentFadeProgress = Math.max(0,
+ TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endContentFadeProgress = Math.min(1,
+ startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+ searchResultView.setAlpha(1 - clampToProgress(mSearchToAzProgress,
+ startContentFadeProgress, endContentFadeProgress));
+
+ // Adjust background (or decorator) alpha based on start progress and stagger.
+ float startBackgroundFadeProgress = Math.max(0,
+ TOP_BACKGROUND_FADE_PROGRESS_START
+ - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endBackgroundFadeProgress = Math.min(1,
+ startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+ float backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+ startBackgroundFadeProgress, endBackgroundFadeProgress);
+ int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
+ boolean decoratorFilled =
+ adapterPosition != NO_POSITION
+ && searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
+ .setDecorationFillAlpha((int) (255 * backgroundAlpha));
+ if (!decoratorFilled) {
+ // Try to adjust background alpha instead (e.g. for Search Edu card).
+ Drawable background = searchResultView.getBackground();
+ if (background != null) {
+ background.setAlpha((int) (255 * backgroundAlpha));
+ }
+ }
+
+ float scaleY = 1 - mSearchToAzProgress;
+ int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
+ searchResultView.setScaleY(scaleY);
+ searchResultView.setY(top + totalHeight);
+
+ numSearchResultsAnimated++;
+ totalHeight += scaledHeight;
+ }
+
+ return totalHeight - appRowHeight;
+ }
+
+ /** Called just before a child is attached to the SearchRecyclerView. */
+ private void onSearchChildAttached(View child) {
+ // Avoid allocating hardware layers for alpha changes.
+ child.forceHasOverlappingRendering(false);
+ if (mSearchToAzProgress > 0) {
+ // Before the child is rendered, apply the animation including it to avoid flicker.
+ updateSearchRecyclerViewProgress();
+ } else {
+ // Apply default states without processing the full layout.
+ child.setAlpha(1);
+ child.setScaleY(1);
+ child.setTranslationY(0);
+ int adapterPosition = getSearchRecyclerView().getChildAdapterPosition(child);
+ if (adapterPosition != NO_POSITION) {
+ getSearchRecyclerView().getApps().getAdapterItems().get(adapterPosition)
+ .setDecorationFillAlpha(255);
+ }
+ if (child.getBackground() != null) {
+ child.getBackground().setAlpha(255);
+ }
+ }
+ }
+
+ private float getSearchToAzProgress() {
+ return mSearchToAzProgress;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index fedc91f..a589448 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -170,12 +170,14 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (Utilities.ATLEAST_R && isEnabled()) {
+ if (!Utilities.ATLEAST_R) {
+ return insets;
+ }
+ if (insets.isVisible(WindowInsets.Type.ime())) {
+ Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
+ setTranslationY(mInsets.bottom - keyboardInsets.bottom);
+ } else {
setTranslationY(0);
- if (insets.isVisible(WindowInsets.Type.ime())) {
- Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
- setTranslationY(mInsets.bottom - keyboardInsets.bottom);
- }
}
return insets;
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 52d8f63..78c305b 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -168,14 +168,11 @@
public void onSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) {
mAppsView.setSearchResults(items);
- mAppsView.setLastSearchQuery(query);
}
}
@Override
public void clearSearchResult() {
- mAppsView.setSearchResults(null);
-
// Clear the search query
mSearchQueryBuilder.clear();
mSearchQueryBuilder.clearSpans();
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index a312070..6196df2 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import androidx.annotation.CallSuper;
+
/**
* Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
*/
@@ -27,6 +29,7 @@
protected boolean mCancelled = false;
@Override
+ @CallSuper
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index a643196..ffce570 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -288,6 +288,11 @@
public static final BooleanFlag SHOW_SEARCH_EDUCARD_QSB = new DeviceFlag(
"SHOW_SEARCH_EDUCARD_QSB", false, "Shows Search Educard for QSB entry in OneSearch.");
+ public static final BooleanFlag ENABLE_IME_LATENCY_LOGGER = getDebugFlag(
+ "ENABLE_IME_LATENCY_LOGGER", false,
+ "Enable option to log the keyboard latency for both atomic and controlled keyboard "
+ + "animations on an EditText");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/logging/KeyboardStateManager.java b/src/com/android/launcher3/logging/KeyboardStateManager.java
new file mode 100644
index 0000000..3103af1
--- /dev/null
+++ b/src/com/android/launcher3/logging/KeyboardStateManager.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.logging;
+
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.NO_IME_ACTION;
+
+import android.os.SystemClock;
+
+/**
+ * Class to maintain keyboard states.
+ */
+public class KeyboardStateManager {
+ private long mUpdatedTime;
+
+ public enum KeyboardState {
+ NO_IME_ACTION,
+ SHOW,
+ HIDE,
+ }
+
+ private KeyboardState mKeyboardState;
+
+ public KeyboardStateManager() {
+ mKeyboardState = NO_IME_ACTION;
+ }
+
+ /**
+ * Returns time when keyboard state was updated.
+ */
+ public long getLastUpdatedTime() {
+ return mUpdatedTime;
+ }
+
+ /**
+ * Returns current keyboard state.
+ */
+ public KeyboardState getKeyboardState() {
+ return mKeyboardState;
+ }
+
+ /**
+ * Setter method to set keyboard state.
+ */
+ public void setKeyboardState(KeyboardState keyboardState) {
+ mUpdatedTime = SystemClock.elapsedRealtime();
+ mKeyboardState = keyboardState;
+ }
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index c4ec4e3..651372f 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -56,6 +56,7 @@
private InstanceId mInstanceId;
protected @Nullable ActivityContext mActivityContext = null;
+ private KeyboardStateManager mKeyboardStateManager;
/**
* Returns event enum based on the two state transition information when swipe
@@ -816,6 +817,16 @@
return logger;
}
+ /**
+ * Returns a singleton KeyboardStateManager.
+ */
+ public KeyboardStateManager keyboardStateManager() {
+ if (mKeyboardStateManager == null) {
+ mKeyboardStateManager = new KeyboardStateManager();
+ }
+ return mKeyboardStateManager;
+ }
+
protected StatsLogger createLogger() {
return new StatsLogger() {
};
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index c25929a..341372e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -31,6 +31,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.InvariantDeviceProfile;
@@ -47,6 +48,7 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -776,17 +778,6 @@
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
- /**
- * This method should return an id that should be the same for two folders containing the
- * same elements.
- */
- private String getFolderMigrationId() {
- return mFolderItems.keySet().stream()
- .map(intentString -> mFolderItems.get(intentString).size() + intentString)
- .sorted()
- .collect(Collectors.joining(","));
- }
-
/** This id is not used in the DB is only used while doing the migration and it identifies
* an entry on each workspace. For example two calculator icons would have the same
* migration id even thought they have different database ids.
@@ -797,9 +788,47 @@
return getFolderMigrationId();
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
return mProvider;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ final String intentStr = cleanIntentString(mIntent);
+ try {
+ Intent i = Intent.parseUri(intentStr, 0);
+ return Objects.requireNonNull(i.getComponent()).toString();
+ } catch (Exception e) {
+ return intentStr;
+ }
default:
- return mIntent;
+ return cleanIntentString(mIntent);
}
}
+
+ /**
+ * This method should return an id that should be the same for two folders containing the
+ * same elements.
+ */
+ @NonNull
+ private String getFolderMigrationId() {
+ return mFolderItems.keySet().stream()
+ .map(intentString -> mFolderItems.get(intentString).size()
+ + cleanIntentString(intentString))
+ .sorted()
+ .collect(Collectors.joining(","));
+ }
+
+ /**
+ * This is needed because sourceBounds can change and make the id of two equal items
+ * different.
+ */
+ @NonNull
+ private String cleanIntentString(@NonNull String intentStr) {
+ try {
+ Intent i = Intent.parseUri(intentStr, 0);
+ i.setSourceBounds(null);
+ return i.toURI();
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Unable to parse Intent string", e);
+ return intentStr;
+ }
+
+ }
}
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index c44e1e1..54e8e5b 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -345,6 +345,7 @@
@Override
public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
onStateTransitionFailed(state);
}
};
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index b63715c..3286afb 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -31,8 +31,7 @@
private static final int STATE_FLAGS = FLAG_MULTI_PAGE
| FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
- | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS
- | FLAG_HIDE_BACK_BUTTON;
+ | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS;
public SpringLoadedState(int id) {
super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 9ac1c0e..89c300d 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -376,6 +376,19 @@
return isRtl ? 1 : -1;
}
+ @Override
+ public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+ DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
+ float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
+ ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
+ : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
+ float additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
+ * topLeftTaskPlusDividerPercent;
+ taskMenuView.setY(taskMenuView.getY() + additionalOffset);
+ }
+
/* -------------------- */
@Override
@@ -492,8 +505,8 @@
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
int dividerBar = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.visualDividerBounds.height()
- : splitBoundsConfig.visualDividerBounds.width();
+ ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight)
+ : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth);
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 1a8d355..cbcb700 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -231,6 +231,14 @@
int getTaskDragDisplacementFactor(boolean isRtl);
/**
+ * Calls the corresponding {@link View#setX(float)} or {@link View#setY(float)}
+ * on {@param taskMenuView} by taking the space needed by {@param primarySnapshotView} into
+ * account.
+ * This is expected to only be called for secondary (bottom/right) tasks.
+ */
+ void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+ DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView);
+ /**
* Maps the velocity from the coordinate plane of the foreground app to that
* of Launcher's (which now will always be portrait)
*/
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 07d1839..450205d 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -316,6 +316,26 @@
}
@Override
+ public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+ DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
+ float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
+ ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
+ : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
+ float additionalOffset;
+ if (deviceProfile.isLandscape) {
+ additionalOffset = (taskView.getWidth() - snapshotParams.leftMargin)
+ * topLeftTaskPlusDividerPercent;
+ taskMenuView.setX(taskMenuView.getX() + additionalOffset);
+ } else {
+ additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
+ * topLeftTaskPlusDividerPercent;
+ taskMenuView.setY(taskMenuView.getY() + additionalOffset);
+ }
+ }
+
+ @Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 6bc26e7..1cb0752 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -40,4 +40,9 @@
* When turned on, we enable suggest related logging.
*/
public static final String SEARCH_LOGGING = "SearchLogging";
+
+ /**
+ * When turned on, we enable IME related latency related logging.
+ */
+ public static final String IME_LATENCY_LOGGING = "ImeLatencyLogging";
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 7e6711f..706b51f 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,24 +15,12 @@
*/
package com.android.launcher3.util;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowInsetsController;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.views.ActivityContext;
/**
* Utility class for offloading some class from UI thread
@@ -43,64 +31,14 @@
new MainThreadInitializedObject<>(
c -> new Handler(UI_HELPER_EXECUTOR.getLooper(), new UiCallbacks(c)));
- private static final int MSG_HIDE_KEYBOARD = 1;
- private static final int MSG_SET_ORIENTATION = 2;
- private static final int MSG_RUN_COMMAND = 3;
- private static final String STATS_LOGGER_KEY = "STATS_LOGGER_KEY";
-
- @SuppressLint("NewApi")
- public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
- View root = activityContext.getDragLayer();
-
- if (Utilities.ATLEAST_R) {
- Preconditions.assertUIThread();
- // Hide keyboard with WindowInsetsController if could. In case
- // hideSoftInputFromWindow may get ignored by input connection being finished
- // when the screen is off.
- //
- // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
- // longer need to post to the message queue.
- final WindowInsetsController wic = root.getWindowInsetsController();
- WindowInsets insets = root.getRootWindowInsets();
- boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
- if (wic != null && isImeShown) {
- // this method cannot be called cross threads
- wic.hide(WindowInsets.Type.ime());
- activityContext.getStatsLogManager().logger()
- .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
- return;
- }
- }
- // Since the launcher context cannot be accessed directly from callback, adding secondary
- // message to log keyboard close event asynchronously.
- Bundle mHideKeyboardLoggerMsg = new Bundle();
- mHideKeyboardLoggerMsg.putParcelable(
- STATS_LOGGER_KEY,
- Message.obtain(
- HANDLER.get(root.getContext()),
- () -> activityContext
- .getStatsLogManager()
- .logger()
- .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
- )
- );
-
- Message mHideKeyboardMsg = Message.obtain(HANDLER.get(root.getContext()), MSG_HIDE_KEYBOARD,
- token);
- mHideKeyboardMsg.setData(mHideKeyboardLoggerMsg);
- mHideKeyboardMsg.sendToTarget();
- }
+ private static final int MSG_SET_ORIENTATION = 1;
+ private static final int MSG_RUN_COMMAND = 2;
public static void setOrientationAsync(Activity activity, int orientation) {
Message.obtain(HANDLER.get(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
.sendToTarget();
}
- public static void setBackButtonAlphaAsync(Context context, AsyncCommand command, float alpha,
- boolean animate) {
- runAsyncCommand(context, command, Float.floatToIntBits(alpha), animate ? 1 : 0);
- }
-
public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
Message.obtain(HANDLER.get(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
}
@@ -108,23 +46,14 @@
private static class UiCallbacks implements Handler.Callback {
private final Context mContext;
- private final InputMethodManager mIMM;
UiCallbacks(Context context) {
mContext = context;
- mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
- case MSG_HIDE_KEYBOARD:
- if (mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0)) {
- // log keyboard close event only when keyboard is actually closed
- ((Message) message.getData().getParcelable(STATS_LOGGER_KEY))
- .sendToTarget();
- }
- return true;
case MSG_SET_ORIENTATION:
((Activity) message.obj).setRequestedOrientation(message.arg1);
return true;
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index e2dc34f..dd5b22e 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,16 +15,26 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
+import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
@@ -36,6 +46,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ViewCache;
/**
@@ -202,4 +213,47 @@
ActivityAllAppsContainerView<?> appsView) {
return null;
}
+
+ /**
+ * Hides the keyboard if it is visible
+ */
+ default void hideKeyboard() {
+ View root = getDragLayer();
+ if (root == null) {
+ return;
+ }
+ if (Utilities.ATLEAST_R) {
+ Preconditions.assertUIThread();
+ // Hide keyboard with WindowInsetsController if could. In case
+ // hideSoftInputFromWindow may get ignored by input connection being finished
+ // when the screen is off.
+ //
+ // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
+ // longer need to post to the message queue.
+ final WindowInsetsController wic = root.getWindowInsetsController();
+ WindowInsets insets = root.getRootWindowInsets();
+ boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
+ if (wic != null && isImeShown) {
+ StatsLogManager slm = getStatsLogManager();
+ slm.keyboardStateManager().setKeyboardState(HIDE);
+
+ // this method cannot be called cross threads
+ wic.hide(WindowInsets.Type.ime());
+ slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+ return;
+ }
+ }
+
+ InputMethodManager imm = root.getContext().getSystemService(InputMethodManager.class);
+ IBinder token = root.getWindowToken();
+ if (imm != null && token != null) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (imm.hideSoftInputFromWindow(token, 0)) {
+ // log keyboard close event only when keyboard is actually closed
+ MAIN_EXECUTOR.execute(() ->
+ getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED));
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6e6512d..40e4ce1 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -20,8 +20,6 @@
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
-
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -313,7 +311,7 @@
}
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
mIsDragging = true;
if (mCanThumbDetach) {
mIsThumbDetached = true;
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 5baa7d2..be49974 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -214,9 +214,9 @@
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
isWorkspaceScrollable(launcher)));
- workspace.verifyWorkspaceAppIconIsGone(
- "Chrome app was found on empty workspace", "Chrome");
-
+ assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
+ workspace.getPageCount());
+ workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
workspace.ensureWorkspaceIsScrollable();
executeOnLauncher(
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 5761abd..302bd2f 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+
import android.util.Log;
import android.view.View;
@@ -48,6 +49,7 @@
private int mProfileUserId;
private boolean mWorkProfileSetupSuccessful;
+ private final String TAG = "WorkProfileTest";
@Before
@Override
@@ -56,18 +58,17 @@
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
- Log.d("b/203817455", "pm create-user; output: " + output);
-
- if (output.startsWith("Success")){
- assertTrue("Failed to create work profile", output.startsWith("Success"));
- mWorkProfileSetupSuccessful = true;
- } else {
- return; // no need to setup launcher since all tests will skip.
- }
+ // b/203817455
+ updateWorkProfileSetupSuccessful("pm create-user", output);
String[] tokens = output.split("\\s+");
mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
- mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ output = mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ updateWorkProfileSetupSuccessful("am start-user", output);
+
+ if (!mWorkProfileSetupSuccessful) {
+ return; // no need to setup launcher since all tests will skip.
+ }
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -107,6 +108,7 @@
@Test
public void workTabExists() {
assumeTrue(mWorkProfileSetupSuccessful);
+ waitForWorkTabSetup();
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(),
LauncherInstrumentation.WAIT_TIME_MS);
@@ -134,7 +136,11 @@
LauncherInstrumentation.WAIT_TIME_MS);
//start work profile toggle OFF test
- executeOnLauncher(l -> l.getAppsView().getWorkManager().getWorkModeSwitch().performClick());
+ executeOnLauncher(l -> {
+ // Ensure updates are not deferred so notification happens when apps pause.
+ l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+ l.getAppsView().getWorkManager().getWorkModeSwitch().performClick();
+ });
waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
manager.reset(); // pulls current state from system
@@ -183,4 +189,14 @@
}
}, LauncherInstrumentation.WAIT_TIME_MS);
}
+
+ private void updateWorkProfileSetupSuccessful(String cli, String output) {
+ Log.d(TAG, "updateWorkProfileSetupSuccessful, cli=" + cli + " " + "output=" + output);
+ if (output.startsWith("Success")) {
+ assertTrue(output, output.startsWith("Success"));
+ mWorkProfileSetupSuccessful = true;
+ } else {
+ mWorkProfileSetupSuccessful = false;
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 5f92199..c365708 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,12 +15,21 @@
*/
package com.android.launcher3.tapl;
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
/**
* Operations on home screen qsb.
*/
public class HomeQsb {
private final LauncherInstrumentation mLauncher;
+ private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+
HomeQsb(LauncherInstrumentation launcher) {
mLauncher = launcher;
@@ -28,6 +37,35 @@
}
/**
+ * Launch assistant app by tapping mic icon on qsb.
+ */
+ @NonNull
+ public LaunchedAppState launchAssistant() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click assistant mic icon button");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID);
+
+ LauncherInstrumentation.log("HomeQsb.launchAssistant before click "
+ + assistantIcon.getVisibleCenter() + " in "
+ + mLauncher.getVisibleBounds(assistantIcon));
+
+ mLauncher.clickLauncherObject(assistantIcon);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ // assert Assistant App Launched
+ BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE);
+ mLauncher.assertTrue(
+ "assistant app didn't start: (" + selector + ")",
+ mLauncher.getDevice().wait(Until.hasObject(selector),
+ LauncherInstrumentation.WAIT_TIME_MS)
+ );
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
+ /**
* Show search result page from tapping qsb.
*/
public SearchResultFromQsb showSearchResult() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index efbdb23..5fab7eb 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -192,6 +192,12 @@
}
}
+ /** Returns the number of pages. */
+ public int getPageCount() {
+ final UiObject2 workspace = verifyActiveContainer();
+ return workspace.getChildCount();
+ }
+
/**
* Returns the number of pages that are visible on the screen simultaneously.
*/