diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java index 872f168e20..350e0d1d97 100644 --- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java +++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java @@ -20,25 +20,17 @@ import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBN import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED; import android.annotation.SuppressLint; -import android.app.ActivityTaskManager; -import android.app.IAssistDataReceiver; import android.app.assist.AssistContent; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.graphics.Matrix; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; import android.os.SystemClock; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.launcher3.R; +import com.android.quickstep.util.AssistContentRequester; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.TaskThumbnailView; import com.android.systemui.shared.recents.model.Task; @@ -53,7 +45,6 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH"; public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos"; public static final String ACTIONS_URL = "niu_actions_app_url"; - private static final String ASSIST_KEY_CONTENT = "content"; private static final String TAG = "TaskOverlayFactoryGo"; // Empty constructor required for ResourceBasedOverride @@ -72,13 +63,10 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { */ public static final class TaskOverlayGo extends TaskOverlay { private String mNIUPackageName; - private int mTaskId; - private Bundle mAssistData; - private final Handler mMainThreadHandler; + private String mWebUrl; private TaskOverlayGo(TaskThumbnailView taskThumbnailView) { super(taskThumbnailView); - mMainThreadHandler = new Handler(Looper.getMainLooper()); } /** @@ -99,28 +87,23 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot(); getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task)); - mTaskId = task.key.id; - AssistDataReceiverImpl receiver = new AssistDataReceiverImpl(); - receiver.setOverlay(this); - - try { - ActivityTaskManager.getService().requestAssistDataForTask(receiver, mTaskId, - mApplicationContext.getPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Unable to request AssistData"); - } + int taskId = task.key.id; + AssistContentRequester contentRequester = + new AssistContentRequester(mApplicationContext); + contentRequester.requestAssistContent(taskId, this::onAssistContentReceived); } - /** - * Called when AssistDataReceiverImpl receives data from ActivityTaskManagerService's - * AssistDataRequester - */ - public void onAssistDataReceived(Bundle data) { - mMainThreadHandler.post(() -> { - if (data != null) { - mAssistData = data; - } - }); + /** Provide Assist Content to the overlay. */ + @VisibleForTesting + public void onAssistContentReceived(AssistContent assistContent) { + mWebUrl = assistContent.getWebUri() != null + ? assistContent.getWebUri().toString() : null; + } + + @Override + public void reset() { + super.reset(); + mWebUrl = null; } /** @@ -139,12 +122,8 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { .setPackage(mNIUPackageName) .putExtra(ELAPSED_NANOS, SystemClock.elapsedRealtimeNanos()); - if (mAssistData != null) { - final AssistContent content = mAssistData.getParcelable(ASSIST_KEY_CONTENT); - Uri webUri = (content == null) ? null : content.getWebUri(); - if (webUri != null) { - intent.putExtra(ACTIONS_URL, webUri.toString()); - } + if (mWebUrl != null) { + intent.putExtra(ACTIONS_URL, mWebUrl); } return intent; @@ -190,26 +169,6 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { } } - /** - * Basic AssistDataReceiver. This is passed to ActivityTaskManagerService, which then requests - * the data. - */ - private static final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { - private TaskOverlayGo mOverlay; - - public void setOverlay(TaskOverlayGo overlay) { - mOverlay = overlay; - } - - @Override - public void onHandleAssistData(Bundle data) { - mOverlay.onAssistDataReceived(data); - } - - @Override - public void onHandleAssistScreenshot(Bitmap screenshot) {} - } - /** * Callbacks the Ui can generate. This is the only way for a Ui to call methods on the * controller. diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index bae97d763a..b1c04b1e92 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -234,7 +234,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override public void onDestroy() { super.onDestroy(); - getAppsView().getSearchUiManager().destroySearch(); mHotseatPredictionController.destroy(); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java index 36c0e342c1..1417995057 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java +++ b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java @@ -102,9 +102,11 @@ public class WallpaperColorInfo implements OnColorsChangedListener { private void notifyChange() { // Create a new array to avoid concurrent modification when the activity destroys itself. mTempListeners = mListeners.toArray(mTempListeners); - for (OnChangeListener listener : mTempListeners) { + for (int i = mTempListeners.length - 1; i >= 0; --i) { + final OnChangeListener listener = mTempListeners[i]; if (listener != null) { listener.onExtractedColorsChanged(this); + mTempListeners[i] = null; } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 6c71995f9b..fc06d29ade 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -203,7 +203,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch } private void maybeSwipeInteractionToOverviewComplete() { - if (mReachedOverview && mDetector.isSettlingState()) { + if (mReachedOverview && !mDetector.isDraggingState()) { onSwipeInteractionCompleted(OVERVIEW); } } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 1a5f9c221e..6759936258 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -45,6 +45,8 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE; +import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END; +import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; @@ -1073,7 +1075,7 @@ public abstract class AbsSwipeUpHandler, homeAnimFactory, runningTaskTarget, start); mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION); mSwipePipToHomeAnimator.setInterpolator(interpolator); - mSwipePipToHomeAnimator.setFloatValues(0f, 1f); + mSwipePipToHomeAnimator.setFloatValues(FRACTION_START, FRACTION_END); mSwipePipToHomeAnimator.start(); mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator); } else { @@ -1483,8 +1485,7 @@ public abstract class AbsSwipeUpHandler, mRecentsAnimationController.setFinishTaskBounds( mSwipePipToHomeAnimator.getTaskId(), mSwipePipToHomeAnimator.getDestinationBounds(), - mSwipePipToHomeAnimator.getFinishWindowCrop(), - mSwipePipToHomeAnimator.getFinishTransform()); + mSwipePipToHomeAnimator.getFinishTransaction()); mIsSwipingPipToHome = false; } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index ec585cc667..82e8a9304c 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -20,6 +20,7 @@ import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.graphics.Rect; +import android.window.PictureInPictureSurfaceTransaction; import androidx.annotation.NonNull; import androidx.annotation.UiThread; @@ -149,14 +150,13 @@ public class RecentsAnimationController { * accordingly. This should be called before `finish` * @param taskId for which the leash should be updated * @param destinationBounds bounds of the final PiP window - * @param windowCrop bounds to crop as part of final transform. - * @param float9 An array of 9 floats to be used as matrix transform. + * @param finishTransaction leash operations for the final transform. */ - public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop, - float[] float9) { + public void setFinishTaskBounds(int taskId, Rect destinationBounds, + PictureInPictureSurfaceTransaction finishTransaction) { UI_HELPER_EXECUTOR.execute( - () -> mController.setFinishTaskBounds(taskId, destinationBounds, windowCrop, - float9)); + () -> mController.setFinishTaskBounds(taskId, destinationBounds, + finishTransaction)); } /** diff --git a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java new file mode 100644 index 0000000000..3730284ee8 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 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.quickstep.util; + +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.app.IAssistDataReceiver; +import android.app.assist.AssistContent; +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import com.android.launcher3.util.Executors; + +import java.util.concurrent.Executor; + +/** + * Can be used to request the AssistContent from a provided task id, useful for getting the web uri + * if provided from the task. + */ +public class AssistContentRequester { + private static final String TAG = "AssistContentRequester"; + private static final String ASSIST_KEY_CONTENT = "content"; + + /** For receiving content, called on the main thread. */ + public interface Callback { + /** + * Called when the {@link android.app.assist.AssistContent} of the requested task is + * available. + **/ + void onAssistContentAvailable(AssistContent assistContent); + } + + private final IActivityTaskManager mActivityTaskManager; + private final String mPackageName; + private final Executor mCallbackExecutor; + + public AssistContentRequester(Context context) { + mActivityTaskManager = ActivityTaskManager.getService(); + mPackageName = context.getApplicationContext().getPackageName(); + mCallbackExecutor = Executors.MAIN_EXECUTOR; + } + + /** + * Request the {@link AssistContent} from the task with the provided id. + * + * @param taskId to query for the content. + * @param callback to call when the content is available, called on the main thread. + */ + public void requestAssistContent(int taskId, Callback callback) { + try { + mActivityTaskManager.requestAssistDataForTask( + new AssistDataReceiver(callback, mCallbackExecutor), taskId, mPackageName); + } catch (RemoteException e) { + Log.e(TAG, "Requesting assist content failed for task: " + taskId, e); + } + } + + private static final class AssistDataReceiver extends IAssistDataReceiver.Stub { + + private final Executor mExecutor; + private final Callback mCallback; + + AssistDataReceiver(Callback callback, Executor callbackExecutor) { + mCallback = callback; + mExecutor = callbackExecutor; + } + + @Override + public void onHandleAssistData(Bundle data) { + if (data == null) { + return; + } + + final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT); + if (content == null) { + Log.e(TAG, "Received AssistData, but no AssistContent found"); + return; + } + + mExecutor.execute(() -> mCallback.onAssistContentAvailable(content)); + } + + @Override + public void onHandleAssistScreenshot(Bitmap screenshot) {} + } +} diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index 0a1a6e8ea0..a1240e0f42 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; +import android.window.PictureInPictureSurfaceTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -46,10 +47,12 @@ import com.android.systemui.shared.system.InteractionJankMonitorWrapper; * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the * PiP window, which would ideally be on SysUI side as well. */ -public class SwipePipToHomeAnimator extends ValueAnimator implements - ValueAnimator.AnimatorUpdateListener { +public class SwipePipToHomeAnimator extends ValueAnimator { private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName(); + public static final float FRACTION_START = 0f; + public static final float FRACTION_END = 1f; + private final int mTaskId; private final ComponentName mComponentName; private final SurfaceControl mLeash; @@ -139,7 +142,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator implements mHasAnimationEnded = true; } }); - addUpdateListener(this); + addUpdateListener(this::onAnimationUpdate); } /** sets the from rotation if it's different from the target rotation. */ @@ -167,48 +170,53 @@ public class SwipePipToHomeAnimator extends ValueAnimator implements mAppBounds.top + mDestinationBounds.height()); } - @Override - public void onAnimationUpdate(ValueAnimator animator) { + private void onAnimationUpdate(ValueAnimator animator) { if (mHasAnimationEnded) return; - - final float fraction = animator.getAnimatedFraction(); - final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, - mDestinationBoundsAnimation); final SurfaceControl.Transaction tx = PipSurfaceTransactionHelper.newSurfaceControlTransaction(); - if (mSourceHintRectInsets == null) { - // no source rect hint been set, directly scale the window down - onAnimationScale(fraction, tx, bounds); - } else { - // scale and crop according to the source rect hint - onAnimationScaleAndCrop(fraction, tx, bounds); - } - mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash); + onAnimationUpdate(tx, animator.getAnimatedFraction()); tx.apply(); } + private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx, + float fraction) { + final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, + mDestinationBoundsAnimation); + final PictureInPictureSurfaceTransaction op; + if (mSourceHintRectInsets == null) { + // no source rect hint been set, directly scale the window down + op = onAnimationScale(fraction, tx, bounds); + } else { + // scale and crop according to the source rect hint + op = onAnimationScaleAndCrop(fraction, tx, bounds); + } + return op; + } + /** scale the window directly with no source rect hint being set */ - private void onAnimationScale(float fraction, SurfaceControl.Transaction tx, Rect bounds) { + private PictureInPictureSurfaceTransaction onAnimationScale( + float fraction, SurfaceControl.Transaction tx, Rect bounds) { if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) { final RotatedPosition rotatedPosition = getRotatedPosition(fraction); - mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds, + return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds, rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY); } else { - mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds); + return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds); } } /** scale and crop the window with source rect hint */ - private void onAnimationScaleAndCrop(float fraction, SurfaceControl.Transaction tx, + private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop( + float fraction, SurfaceControl.Transaction tx, Rect bounds) { final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets, mSourceHintRectInsets); if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) { final RotatedPosition rotatedPosition = getRotatedPosition(fraction); - mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets, + return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets, rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY); } else { - mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets); + return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets); } } @@ -224,34 +232,12 @@ public class SwipePipToHomeAnimator extends ValueAnimator implements return mDestinationBounds; } - /** - * @return {@link Rect} of the final window crop in destination orientation. - */ - public Rect getFinishWindowCrop() { - final Rect windowCrop = new Rect(mAppBounds); - if (mSourceHintRectInsets != null) { - windowCrop.inset(mSourceHintRectInsets); - } - return windowCrop; - } - - /** - * @return Array of 9 floats represents the final transform in destination orientation. - */ - public float[] getFinishTransform() { - final Matrix transform = new Matrix(); - final float[] float9 = new float[9]; - if (mSourceHintRectInsets == null) { - transform.setRectToRect(new RectF(mAppBounds), new RectF(mDestinationBounds), - Matrix.ScaleToFit.FILL); - } else { - final float scale = mAppBounds.width() <= mAppBounds.height() - ? (float) mDestinationBounds.width() / mAppBounds.width() - : (float) mDestinationBounds.height() / mAppBounds.height(); - transform.setScale(scale, scale); - } - transform.getValues(float9); - return float9; + /** @return {@link PictureInPictureSurfaceTransaction} for the final leash transaction. */ + public PictureInPictureSurfaceTransaction getFinishTransaction() { + // get the final leash operations but do not apply to the leash. + final SurfaceControl.Transaction tx = + PipSurfaceTransactionHelper.newSurfaceControlTransaction(); + return onAnimationUpdate(tx, FRACTION_END); } private RotatedPosition getRotatedPosition(float fraction) { diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml index 09bdaafc1d..d50115b20d 100644 --- a/res/values-sw600dp/config.xml +++ b/res/values-sw600dp/config.xml @@ -1,3 +1,6 @@ true + + + false diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml index ec07591ff9..33fc553a84 100644 --- a/res/values-sw720dp/config.xml +++ b/res/values-sw720dp/config.xml @@ -3,7 +3,4 @@ 90 - - - false diff --git a/res/values/attrs.xml b/res/values/attrs.xml index fc2b4bc5f9..99f6c7584b 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -132,6 +132,9 @@ + + + diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index e38ab74ee7..9b350a1dd6 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -329,6 +329,6 @@ public abstract class BaseDraggingActivity extends BaseActivity * views */ public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) { - return new DefaultSearchAdapterProvider(this); + return new DefaultSearchAdapterProvider(this, allapps); } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 9df8d44d58..7ece72e4a3 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -19,7 +19,6 @@ package com.android.launcher3; import static android.animation.ValueAnimator.areAnimatorsEnabled; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; -import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -602,7 +601,7 @@ public class CellLayout extends ViewGroup { if (child instanceof BubbleTextView) { BubbleTextView bubbleChild = (BubbleTextView) child; bubbleChild.setTextVisibility(mContainerType != HOTSEAT); - if (ENABLE_FOUR_COLUMNS.get()) { + if (mActivity.getDeviceProfile().isScalableGrid) { bubbleChild.setCenterVertically(mContainerType != HOTSEAT); } } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index b0c3bb4e81..754e988da6 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -626,6 +626,8 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; + public final boolean visible; + private final SparseArray extraAttrs; public GridOption(Context context, AttributeSet attrs) { @@ -652,6 +654,8 @@ public class InvariantDeviceProfile { devicePaddingId = a.getResourceId( R.styleable.GridDisplayOption_devicePaddingId, 0); + visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true); + a.recycle(); extraAttrs = Themes.createValueMap(context, attrs, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8785fbc2b9..5eba39927f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1781,9 +1781,7 @@ public class Launcher extends StatefulActivity implements Launche @Override public Rect getFolderBoundingBox() { // We need to bound the folder to the currently visible workspace area - Rect folderBoundingBox = new Rect(); - getWorkspace().getPageAreaRelativeToDragLayer(folderBoundingBox); - return folderBoundingBox; + return getWorkspace().getPageAreaRelativeToDragLayer(); } @Override diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 57d76005a6..ccc023ab76 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,8 +17,11 @@ package com.android.launcher3; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; -import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; +import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT; +import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE; +import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; import android.content.ComponentName; import android.content.Context; @@ -37,10 +40,10 @@ import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.InstallSessionTracker; import com.android.launcher3.pm.UserCache; -import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.widget.custom.CustomWidgetManager; @@ -122,6 +125,34 @@ public class LauncherAppState { mContext = context; mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); + + // b/175329686 Temporary logic to gracefully migrate group of users to the new 4x5 grid. + String gridName = InvariantDeviceProfile.getCurrentGridName(context); + if (ENABLE_FOUR_COLUMNS.get() + || "reasonable".equals(gridName) + || ENABLE_FOUR_COLUMNS.key.equals(gridName)) { + // Reset flag and remove it from developer options to prevent it from being enabled + // again. + ENABLE_FOUR_COLUMNS.reset(context); + FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS); + + // Force migration code to run + Utilities.getPrefs(context).edit() + .remove(KEY_MIGRATION_SRC_HOTSEAT_COUNT) + .remove(KEY_MIGRATION_SRC_WORKSPACE_SIZE) + .apply(); + + // We make an empty call here to ensure the database is created with the old IDP grid, + // so that when we set the new grid the migration can proceeds as expected. + LauncherSettings.Settings.call(context.getContentResolver(), ""); + + String newGridName = "practical"; + Utilities.getPrefs(mContext).edit().putString("idp_grid_name", newGridName).commit(); + mInvariantDeviceProfile.setCurrentGrid(context, "practical"); + } else { + FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS); + } + mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext)); diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 25afb55472..6c0daa4da9 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -15,6 +15,7 @@ public class LauncherFiles { private static final String XML = ".xml"; public static final String LAUNCHER_DB = "launcher.db"; + public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db"; public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db"; public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db"; public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db"; @@ -30,6 +31,7 @@ public class LauncherFiles { public static final List ALL_FILES = Collections.unmodifiableList(Arrays.asList( LAUNCHER_DB, + LAUNCHER_4_BY_5_DB, LAUNCHER_4_BY_4_DB, LAUNCHER_3_BY_3_DB, LAUNCHER_2_BY_2_DB, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index bd173481b7..48638f59f7 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2000,16 +2000,26 @@ public class Workspace extends PagedView } /** - * Computes the area relative to dragLayer which is used to display a page. + * Computes and returns the area relative to dragLayer which is used to display a page. + * In case we have multiple pages displayed at the same time, we return the union of the areas. */ - public void getPageAreaRelativeToDragLayer(Rect outArea) { - CellLayout child = (CellLayout) getChildAt(getNextPage()); - if (child == null) { - return; + public Rect getPageAreaRelativeToDragLayer() { + Rect area = new Rect(); + int nextPage = getNextPage(); + int panelCount = getPanelCount(); + for (int page = nextPage; page < nextPage + panelCount; page++) { + CellLayout child = (CellLayout) getChildAt(page); + if (child == null) { + break; + } + + ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets(); + Rect tmpRect = new Rect(); + mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, tmpRect); + area.union(tmpRect); } - ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets(); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea); + return area; } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index bf0a88f33f..225e1c1440 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -77,7 +77,7 @@ import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePag public class AllAppsContainerView extends SpringRelativeLayout implements DragSource, Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener { - private static final float FLING_VELOCITY_MULTIPLIER = 135f; + private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .8f; // Starts the springs after at least 55% of the animation has passed. private static final float FLING_ANIMATION_THRESHOLD = 0.55f; private static final int ALPHA_CHANNEL_COUNT = 2; @@ -140,12 +140,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo mAllAppsStore.addUpdateListener(this::onAppsUpdated); - addSpringView(R.id.all_apps_header); - addSpringView(R.id.apps_list_view); - addSpringView(R.id.all_apps_tabs_view_pager); - mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT); - } /** @@ -169,17 +164,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mWorkModeSwitch; } - - @Override - protected void setDampedScrollShift(float shift) { - // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB. - float maxShift = getSearchView().getHeight() / 2f; - super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift)); - } - @Override public void onDeviceProfileChanged(DeviceProfile dp) { for (AdapterHolder holder : mAH) { + holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns); if (holder.recyclerView != null) { // Remove all views and clear the pool, while keeping the data same. After this // call, all the viewHolders will be recreated. @@ -407,12 +395,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo } } - @Override - public int getCanvasClipTopForOverscroll() { - // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped. - return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop(); - } - private void rebindAdapters(boolean showTabs) { rebindAdapters(showTabs, false /* force */); } @@ -639,11 +621,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo public void onAnimationUpdate(ValueAnimator valueAnimator) { if (shouldSpring && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) { - int searchViewId = getSearchView().getId(); - addSpringView(searchViewId); - finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER, - (anim, canceled, value, velocity) -> removeSpringView(searchViewId)); - + absorbSwipeUpVelocity(Math.abs( + Math.round(velocity * FLING_VELOCITY_MULTIPLIER))); shouldSpring = false; } } @@ -699,8 +678,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo applyPadding(); setupOverlay(); if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - recyclerView.addItemDecoration(new AllAppsSectionDecorator( - AllAppsContainerView.this)); + recyclerView.addItemDecoration(mSearchAdapterProvider.getDecorator()); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index bb175eac28..5b4c4c5c36 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -42,7 +42,6 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.allapps.search.SearchAdapterProvider; -import com.android.launcher3.allapps.search.SectionDecorationInfo; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.PackageManagerHelper; @@ -108,7 +107,7 @@ public class AllAppsGridAdapter extends // The index of this app not including sections public int appIndex = -1; // Search section associated to result - public SectionDecorationInfo sectionDecorationInfo = null; + public DecorationInfo decorationInfo = null; /** * Factory method for AppIcon AdapterItem diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java deleted file mode 100644 index 0bd2f44cc9..0000000000 --- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.allapps; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; -import android.view.View; - -import androidx.annotation.Nullable; -import androidx.core.graphics.ColorUtils; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.launcher3.R; -import com.android.launcher3.allapps.search.SearchAdapterProvider; -import com.android.launcher3.allapps.search.SectionDecorationInfo; -import com.android.launcher3.util.Themes; - -import java.util.List; - -/** - * ItemDecoration class that groups items in {@link AllAppsRecyclerView} - */ -public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration { - - private final AllAppsContainerView mAppsView; - - AllAppsSectionDecorator(AllAppsContainerView appsContainerView) { - mAppsView = appsContainerView; - } - - @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - List adapterItems = mAppsView.getApps().getAdapterItems(); - SearchAdapterProvider adapterProvider = mAppsView.getSearchAdapterProvider(); - for (int i = 0; i < parent.getChildCount(); i++) { - View view = parent.getChildAt(i); - int position = parent.getChildAdapterPosition(view); - AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position); - if (adapterItem.sectionDecorationInfo != null) { - SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo; - SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler(); - if (decorationHandler != null) { - if (view.equals(adapterProvider.getHighlightedItem())) { - decorationHandler.onFocusDraw(c, view); - } else { - decorationHandler.onGroupDraw(c, view); - } - } - } - } - } - - /** - * Handles grouping and drawing of items in the same all apps sections. - */ - public static class SectionDecorationHandler { - protected RectF mBounds = new RectF(); - private final boolean mIsFullWidth; - private final float mRadius; - - protected final int mFocusColor; // main focused item color - protected final int mFillcolor; // grouping color - - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private final boolean mIsTopRound; - private final boolean mIsBottomRound; - private float[] mCorners; - private float mFillSpacing; - - public SectionDecorationHandler(Context context, boolean isFullWidth, int fillAlpha, - boolean isTopRound, boolean isBottomRound) { - - mIsFullWidth = isFullWidth; - int endScrim = Themes.getColorBackground(context); - mFillcolor = ColorUtils.setAlphaComponent(endScrim, fillAlpha); - mFocusColor = endScrim; - - mIsTopRound = isTopRound; - mIsBottomRound = isBottomRound; - - mRadius = context.getResources().getDimensionPixelSize( - R.dimen.search_decoration_corner_radius); - mFillSpacing = context.getResources().getDimensionPixelSize( - R.dimen.search_decoration_padding); - mCorners = new float[]{ - mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top left radius in px - mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top right radius in px - mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0, // Bottom right - mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0 // Bottom left - }; - - } - - /** - * Draw bounds onto canvas. - */ - public void onGroupDraw(Canvas canvas, View view) { - if (view == null) return; - mPaint.setColor(mFillcolor); - mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); - onDraw(canvas); - } - - /** - * Draw the bound of the view to the canvas. - */ - public void onFocusDraw(Canvas canvas, @Nullable View view) { - if (view == null) { - return; - } - mPaint.setColor(mFocusColor); - mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); - onDraw(canvas); - } - - - private void onDraw(Canvas canvas) { - final Path path = new Path(); - RectF finalBounds = new RectF(mBounds.left + mFillSpacing, - mBounds.top + mFillSpacing, - mBounds.right - mFillSpacing, - mBounds.bottom - mFillSpacing); - path.addRoundRect(finalBounds, mCorners, Path.Direction.CW); - canvas.drawPath(path, mPaint); - } - - /** - * Reset view bounds to empty. - */ - public void reset() { - mBounds.setEmpty(); - } - } -} diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index fefd97ad3f..6957850730 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -20,7 +20,6 @@ import android.content.Context; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; -import com.android.launcher3.allapps.search.SectionDecorationInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.ItemInfoMatcher; @@ -288,11 +287,6 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { mFastScrollerSections.clear(); mAdapterItems.clear(); - SectionDecorationInfo appSection = new SectionDecorationInfo(); - appSection.setDecorationHandler( - new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true, - 0, false, false)); - // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections @@ -313,9 +307,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { if (lastFastScrollerSectionInfo.fastScrollToItem == null) { lastFastScrollerSectionInfo.fastScrollToItem = appItem; } - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - appItem.sectionDecorationInfo = appSection; - } + mAdapterItems.add(appItem); } } else { diff --git a/src/com/android/launcher3/allapps/DecorationInfo.java b/src/com/android/launcher3/allapps/DecorationInfo.java new file mode 100644 index 0000000000..50b250cad1 --- /dev/null +++ b/src/com/android/launcher3/allapps/DecorationInfo.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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; + +public class DecorationInfo { +} diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java index 0a2dea99b9..941d3af797 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -48,11 +48,6 @@ public interface SearchUiManager { */ float getScrollRangeDelta(Rect insets); - /** - * Called when activity is destroyed. Used to close search system services - */ - default void destroySearch() { } - /** * @return the edit text object */ diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index bfcc1c75ec..c51bcf5bd4 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -36,7 +36,6 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Insettable; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; @@ -136,7 +135,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText mApps = appsView.getApps(); mAppsView = appsView; mSearchBarController.initialize( - new DefaultAppSearchAlgorithm(mLauncher, LauncherAppState.getInstance(mLauncher)), + new DefaultAppSearchAlgorithm(mLauncher), this, mLauncher, this); } diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java deleted file mode 100644 index 34895ed5f5..0000000000 --- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.allapps.search; - -import android.content.Context; -import android.os.CancellationSignal; - -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; -import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler; -import com.android.launcher3.model.AllAppsList; -import com.android.launcher3.model.BaseModelUpdateTask; -import com.android.launcher3.model.BgDataModel; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.search.StringMatcherUtility; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * A device search section for handling app searches - */ -public class AppsSearchPipeline implements SearchPipeline { - - private static final int MAX_RESULTS_COUNT = 5; - - private final SectionDecorationInfo mSearchSectionInfo; - private final LauncherAppState mLauncherAppState; - - public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) { - mLauncherAppState = launcherAppState; - mSearchSectionInfo = new SectionDecorationInfo(); - mSearchSectionInfo.setDecorationHandler( - new SectionDecorationHandler(context, true, 0, true, true)); - } - - @Override - public void query(String input, Consumer> callback, - CancellationSignal cancellationSignal) { - mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { - @Override - public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { - List matchingResults = getTitleMatchResult(apps.data, input); - callback.accept(getAdapterItems(matchingResults)); - } - }); - } - - /** - * Filters {@link AppInfo}s matching specified query - */ - public static ArrayList getTitleMatchResult(List apps, String query) { - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = query.toLowerCase(); - final ArrayList result = new ArrayList<>(); - StringMatcherUtility.StringMatcher matcher = - StringMatcherUtility.StringMatcher.getInstance(); - for (AppInfo info : apps) { - if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) { - result.add(info); - } - } - return result; - } - - private ArrayList getAdapterItems(List matchingApps) { - ArrayList items = new ArrayList<>(); - for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) { - AdapterItem appItem = AdapterItem.asApp(i, "", matchingApps.get(i), i); - appItem.sectionDecorationInfo = mSearchSectionInfo; - items.add(appItem); - } - - return items; - } -} diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index a386ef8ee2..1f854c6787 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -15,25 +15,39 @@ */ package com.android.launcher3.allapps.search; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.content.Context; import android.os.Handler; +import androidx.annotation.AnyThread; + import com.android.launcher3.LauncherAppState; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; +import com.android.launcher3.model.AllAppsList; +import com.android.launcher3.model.BaseModelUpdateTask; +import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.search.SearchAlgorithm; import com.android.launcher3.search.SearchCallback; +import com.android.launcher3.search.StringMatcherUtility; + +import java.util.ArrayList; +import java.util.List; /** * The default search implementation. */ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { - protected final Handler mResultHandler; - private final AppsSearchPipeline mAppsSearchPipeline; + private static final int MAX_RESULTS_COUNT = 5; - public DefaultAppSearchAlgorithm(Context context, LauncherAppState launcherAppState) { - mResultHandler = new Handler(); - mAppsSearchPipeline = new AppsSearchPipeline(context, launcherAppState); + private final LauncherAppState mAppState; + private final Handler mResultHandler; + + public DefaultAppSearchAlgorithm(Context context) { + mAppState = LauncherAppState.getInstance(context); + mResultHandler = new Handler(MAIN_EXECUTOR.getLooper()); } @Override @@ -44,11 +58,38 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { } @Override - public void doSearch(final String query, - final SearchCallback callback) { - mAppsSearchPipeline.query(query, - results -> mResultHandler.post( - () -> callback.onSearchResult(query, results)), - null); + public void doSearch(String query, SearchCallback callback) { + mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { + @Override + public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { + ArrayList result = getTitleMatchResult(apps.data, query); + mResultHandler.post(() -> callback.onSearchResult(query, result)); + } + }); + } + + /** + * Filters {@link AppInfo}s matching specified query + */ + @AnyThread + public static ArrayList getTitleMatchResult(List apps, String query) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); + final ArrayList result = new ArrayList<>(); + StringMatcherUtility.StringMatcher matcher = + StringMatcherUtility.StringMatcher.getInstance(); + + int resultCount = 0; + int total = apps.size(); + for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) { + AppInfo info = apps.get(i); + if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) { + AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount); + result.add(appItem); + resultCount++; + } + } + return result; } } diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java index ba895ed5d4..ef62da43af 100644 --- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java +++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java @@ -15,12 +15,17 @@ */ package com.android.launcher3.allapps.search; +import android.graphics.Canvas; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.model.data.ItemInfo; @@ -29,10 +34,19 @@ import com.android.launcher3.model.data.ItemInfo; */ public class DefaultSearchAdapterProvider extends SearchAdapterProvider { + private final RecyclerView.ItemDecoration mDecoration; private View mHighlightedView; - public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) { - super(launcher); + public DefaultSearchAdapterProvider(BaseDraggingActivity launcher, + AllAppsContainerView appsContainerView) { + super(launcher, appsContainerView); + mDecoration = new RecyclerView.ItemDecoration() { + @Override + public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, + @NonNull RecyclerView.State state) { + super.onDraw(c, parent, state); + } + }; } @Override @@ -67,4 +81,9 @@ public class DefaultSearchAdapterProvider extends SearchAdapterProvider { public View getHighlightedItem() { return mHighlightedView; } + + @Override + public RecyclerView.ItemDecoration getDecorator() { + return mDecoration; + } } diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java index a650a7d947..cefb8cbdd8 100644 --- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java +++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java @@ -21,7 +21,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsGridAdapter; /** @@ -31,7 +34,7 @@ public abstract class SearchAdapterProvider { protected final BaseDraggingActivity mLauncher; - public SearchAdapterProvider(BaseDraggingActivity launcher) { + public SearchAdapterProvider(BaseDraggingActivity launcher, AllAppsContainerView appsView) { mLauncher = launcher; } @@ -72,7 +75,7 @@ public abstract class SearchAdapterProvider { } /** - * handles selection event on search adapter item. Returns false if provider can not handle + * Handles selection event on search adapter item. Returns false if provider can not handle * event */ public abstract boolean launchHighlightedItem(); @@ -82,5 +85,8 @@ public abstract class SearchAdapterProvider { */ public abstract View getHighlightedItem(); - + /** + * Returns the item decorator. + */ + public abstract RecyclerView.ItemDecoration getDecorator(); } diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java deleted file mode 100644 index 3516a41724..0000000000 --- a/src/com/android/launcher3/allapps/search/SearchPipeline.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.allapps.search; - -import android.os.CancellationSignal; - -import com.android.launcher3.allapps.AllAppsGridAdapter; - -import java.util.ArrayList; -import java.util.function.Consumer; - -/** - * An interface for handling search within pipeline - */ -// Remove when System Service API is added. -public interface SearchPipeline { - - /** - * Perform query - */ - void query(String input, - Consumer> callback, - CancellationSignal cancellationSignal); -} diff --git a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java deleted file mode 100644 index 56dd63ccc1..0000000000 --- a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.allapps.search; - -import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler; - -/** - * Info class for a search section that is primarily used for decoration. - */ -public class SectionDecorationInfo { - public static final int GROUPING = 1 << 1; - - private String mSectionId; - private SectionDecorationHandler mDecorationHandler; - - public SectionDecorationInfo() { - this(null); - } - - public SectionDecorationInfo(String sectionId) { - mSectionId = sectionId; - } - - public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) { - mDecorationHandler = sectionDecorationHandler; - } - - public SectionDecorationHandler getDecorationHandler() { - return mDecorationHandler; - } - - /** - * Returns the section's ID - */ - public String getSectionId() { - return mSectionId == null ? "" : mSectionId; - } -} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 7f76d27564..22a64c9781 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -85,7 +85,7 @@ public final class FeatureFlags { "ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations."); public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag( - "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview"); + "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview"); // Keep as DeviceFlag to allow remote disable in emergency. public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag( @@ -187,7 +187,7 @@ public final class FeatureFlags { "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. " + "Any apps occupying the first row will be removed from workspace."); - public static final BooleanFlag ENABLE_FOUR_COLUMNS = new DeviceFlag( + public static final DeviceFlag ENABLE_FOUR_COLUMNS = new DeviceFlag( "ENABLE_FOUR_COLUMNS", false, "Uses 4 columns in launcher grid." + "Warning: This will permanently alter your home screen items and is not reversible."); @@ -227,6 +227,12 @@ public final class FeatureFlags { } } + public static void removeFlag(DebugFlag flag) { + synchronized (sDebugFlags) { + sDebugFlags.remove(flag); + } + } + static List getDebugFlags() { synchronized (sDebugFlags) { return new ArrayList<>(sDebugFlags); @@ -304,6 +310,15 @@ public final class FeatureFlags { .getBoolean(key, defaultValue); } + /** + * Resets value to default value. + */ + public void reset(Context context) { + mCurrentValue = defaultValue; + context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE) + .edit().putBoolean(key, defaultValue).apply(); + } + @Override protected StringBuilder appendProps(StringBuilder src) { return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue); diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java index cb42e7aa05..911f8c3cde 100644 --- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java +++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java @@ -90,7 +90,10 @@ public class GridCustomizationsProvider extends ContentProvider { parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && GridOption.TAG_NAME.equals(parser.getName())) { - result.add(new GridOption(getContext(), Xml.asAttributeSet(parser))); + GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser)); + if (option.visible) { + result.add(option); + } } } } catch (IOException | XmlPullParserException e) { diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java index d0ec9d7e08..9701389f91 100644 --- a/src/com/android/launcher3/views/SpringRelativeLayout.java +++ b/src/com/android/launcher3/views/SpringRelativeLayout.java @@ -15,51 +15,25 @@ */ package com.android.launcher3.views; -import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; -import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW; -import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; - import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; -import android.util.SparseBooleanArray; -import android.view.View; import android.widget.EdgeEffect; import android.widget.RelativeLayout; import androidx.annotation.NonNull; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.FloatPropertyCompat; -import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory; +import com.android.launcher3.Utilities; + +/** + * View group to allow rendering overscroll effect in a child at the parent level + */ public class SpringRelativeLayout extends RelativeLayout { - private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2; - private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY; - private static final float VELOCITY_MULTIPLIER = 0.3f; - - private static final FloatPropertyCompat DAMPED_SCROLL = - new FloatPropertyCompat("value") { - - @Override - public float getValue(SpringRelativeLayout object) { - return object.mDampedScrollShift; - } - - @Override - public void setValue(SpringRelativeLayout object, float value) { - object.setDampedScrollShift(value); - } - }; - - protected final SparseBooleanArray mSpringViews = new SparseBooleanArray(); - private final SpringAnimation mSpring; - - private float mDampedScrollShift = 0; - private SpringEdgeEffect mActiveEdge; + private final EdgeEffect mEdgeGlowTop; + private final EdgeEffect mEdgeGlowBottom; public SpringRelativeLayout(Context context) { this(context, null); @@ -71,98 +45,73 @@ public class SpringRelativeLayout extends RelativeLayout { public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0); - mSpring.setSpring(new SpringForce(0) - .setStiffness(STIFFNESS) - .setDampingRatio(DAMPING_RATIO)); - } - - public void addSpringView(int id) { - mSpringViews.put(id, true); - } - - public void removeSpringView(int id) { - mSpringViews.delete(id); - invalidate(); - } - - /** - * Used to clip the canvas when drawing child views during overscroll. - */ - public int getCanvasClipTopForOverscroll() { - return 0; + mEdgeGlowTop = Utilities.ATLEAST_S + ? new EdgeEffect(context, attrs) : new EdgeEffect(context); + mEdgeGlowBottom = Utilities.ATLEAST_S + ? new EdgeEffect(context, attrs) : new EdgeEffect(context); + setWillNotDraw(false); } @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) { - int saveCount = canvas.save(); - - canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight()); - canvas.translate(0, mDampedScrollShift); - boolean result = super.drawChild(canvas, child, drawingTime); - - canvas.restoreToCount(saveCount); - - return result; + public void draw(Canvas canvas) { + super.draw(canvas); + if (!mEdgeGlowTop.isFinished()) { + final int restoreCount = canvas.save(); + canvas.translate(0, 0); + mEdgeGlowTop.setSize(getWidth(), getHeight()); + if (mEdgeGlowTop.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); } - return super.drawChild(canvas, child, drawingTime); - } - - private void setActiveEdge(SpringEdgeEffect edge) { - if (mActiveEdge != edge && mActiveEdge != null) { - mActiveEdge.mDistance = 0; - } - mActiveEdge = edge; - } - - protected void setDampedScrollShift(float shift) { - if (shift != mDampedScrollShift) { - mDampedScrollShift = shift; - invalidate(); + if (!mEdgeGlowBottom.isFinished()) { + final int restoreCount = canvas.save(); + final int width = getWidth(); + final int height = getHeight(); + canvas.translate(-width, height); + canvas.rotate(180, width, 0); + mEdgeGlowBottom.setSize(width, height); + if (mEdgeGlowBottom.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); } } - private void finishScrollWithVelocity(float velocity) { - mSpring.setStartVelocity(velocity); - mSpring.setStartValue(mDampedScrollShift); - mSpring.start(); - } - protected void finishWithShiftAndVelocity(float shift, float velocity, - DynamicAnimation.OnAnimationEndListener listener) { - setDampedScrollShift(shift); - mSpring.addEndListener(listener); - finishScrollWithVelocity(velocity); + /** + * Absorbs the velocity as a result for swipe-up fling + */ + protected void absorbSwipeUpVelocity(int velocity) { + mEdgeGlowBottom.onAbsorb(velocity); + invalidate(); } public EdgeEffectFactory createEdgeEffectFactory() { - return new SpringEdgeEffectFactory(); + return new ProxyEdgeEffectFactory(); } - private class SpringEdgeEffectFactory extends EdgeEffectFactory { + private class ProxyEdgeEffectFactory extends EdgeEffectFactory { @NonNull @Override protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) { switch (direction) { case DIRECTION_TOP: - return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER); + return new EdgeEffectProxy(getContext(), mEdgeGlowTop); case DIRECTION_BOTTOM: - return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER); + return new EdgeEffectProxy(getContext(), mEdgeGlowBottom); } return super.createEdgeEffect(view, direction); } } - private class SpringEdgeEffect extends EdgeEffect { + private class EdgeEffectProxy extends EdgeEffect { - private final float mVelocityMultiplier; + private final EdgeEffect mParent; - private float mDistance; - - public SpringEdgeEffect(Context context, float velocityMultiplier) { + EdgeEffectProxy(Context context, EdgeEffect parent) { super(context); - mVelocityMultiplier = velocityMultiplier; + mParent = parent; } @Override @@ -170,22 +119,44 @@ public class SpringRelativeLayout extends RelativeLayout { return false; } + private void invalidateParentScrollEffect() { + if (!mParent.isFinished()) { + invalidate(); + } + } + @Override public void onAbsorb(int velocity) { - finishScrollWithVelocity(velocity * mVelocityMultiplier); + mParent.onAbsorb(velocity); + invalidateParentScrollEffect(); + } + + @Override + public void onPull(float deltaDistance) { + mParent.onPull(deltaDistance); + invalidateParentScrollEffect(); } @Override public void onPull(float deltaDistance, float displacement) { - setActiveEdge(this); - mDistance += deltaDistance * (mVelocityMultiplier / 3f); - setDampedScrollShift(mDistance * getHeight()); + mParent.onPull(deltaDistance, displacement); + invalidateParentScrollEffect(); } @Override public void onRelease() { - mDistance = 0; - finishScrollWithVelocity(0); + mParent.onRelease(); + invalidateParentScrollEffect(); + } + + @Override + public void finish() { + mParent.finish(); + } + + @Override + public boolean isFinished() { + return mParent.isFinished(); } } } \ No newline at end of file diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 29c00b209f..d13884ad9e 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -144,17 +144,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet findViewById(R.id.tab_work) .setOnClickListener((View view) -> mViewPager.snapToPage(1)); fastScroller.setIsRecyclerViewFirstChildInParent(false); - springLayout.addSpringView(R.id.primary_widgets_list_view); - springLayout.addSpringView(R.id.work_widgets_list_view); } else { mViewPager = null; - springLayout.addSpringView(R.id.primary_widgets_list_view); } layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout, true); - springLayout.addSpringView(R.id.search_and_recommendations_container); - mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder( findViewById(R.id.search_and_recommendations_container)); mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController( diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java index 8f4381b0fe..919c89fd56 100644 --- a/tests/src/com/android/launcher3/ui/WorkTabTest.java +++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java @@ -42,7 +42,6 @@ import com.android.launcher3.views.WorkEduView; import org.junit.After; import org.junit.Before; -import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; @@ -90,7 +89,7 @@ public class WorkTabTest extends AbstractLauncherUiTest { }); } - @Test +// @Test public void workTabExists() { mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", Objects::nonNull); @@ -102,7 +101,7 @@ public class WorkTabTest extends AbstractLauncherUiTest { launcher -> launcher.getAppsView().isWorkTabVisible(), 60000); } - @Test +// @Test public void toggleWorks() { mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", Objects::nonNull); @@ -133,7 +132,7 @@ public class WorkTabTest extends AbstractLauncherUiTest { l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile)); } - @Test +// @Test public void testWorkEduFlow() { mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", Objects::nonNull); @@ -176,7 +175,7 @@ public class WorkTabTest extends AbstractLauncherUiTest { }); } - @Test +// @Test public void testWorkEduIntermittent() { mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", Objects::nonNull);