Merge "Do not show install shortcut for Settings" into main
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 1aa5d03..3a28444 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -33,6 +33,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
@@ -48,6 +49,19 @@
private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
/**
+ * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All
+ * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall
+ * list of {@link WidgetsListBaseEntry}s is not sorted.
+ *
+ * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
+ */
+ public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
+ Context context,
+ Predicate<WidgetItem> widgetItemFilter) {
+ return EMPTY_WIDGET_LIST;
+ }
+
+ /**
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
* sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
* not sorted. This list is sorted at the UI when using
@@ -88,4 +102,4 @@
Context context, ComponentName provider, UserHandle userHandle) {
return new PackageItemInfo(provider.getPackageName(), userHandle);
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 575ad9e..b9b4461 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -158,7 +158,10 @@
LauncherAppState app = LauncherAppState.getInstance(this);
mModel.update(app, null);
final ArrayList<WidgetsListBaseEntry> widgets =
- mModel.getWidgetsListForPicker(app.getContext());
+ mModel.getFilteredWidgetsListForPicker(
+ app.getContext(),
+ /*widgetItemFilter=*/ item -> item.widgetInfo != null
+ );
MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 9dac89d..2421c94 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -23,6 +23,7 @@
import android.app.ActivityOptions;
import android.view.KeyEvent;
import android.view.View;
+import android.view.animation.AnimationUtils;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -38,6 +39,7 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
import java.util.List;
@@ -148,8 +150,13 @@
return -1;
}
+ TaskbarActivityContext context = mControllers.taskbarActivityContext;
RemoteTransition remoteTransition = new RemoteTransition(new SlideInRemoteTransition(
- Utilities.isRtl(mControllers.taskbarActivityContext.getResources())));
+ Utilities.isRtl(mControllers.taskbarActivityContext.getResources()),
+ context.getDeviceProfile().overviewPageSpacing,
+ QuickStepContract.getWindowCornerRadius(context),
+ AnimationUtils.loadInterpolator(
+ context, android.R.interpolator.fast_out_extra_slow_in)));
if (mOnDesktop) {
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 8092582..d834935 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_WIDGET_ATTEMPT;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -59,6 +60,9 @@
remoteResponse.getLaunchOptions(view));
}
if (mLauncher.isSplitSelectionEnabled()) {
+ // Log metric
+ StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
+ logger.log(LAUNCHER_SPLIT_WIDGET_ATTEMPT);
Toast.makeText(hostView.getContext(), R.string.split_widgets_not_supported,
Toast.LENGTH_SHORT).show();
return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 6651c73..0650f9d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -110,10 +110,13 @@
clampToProgress(LINEAR, 0, 0.33f));
}
+ // We sync the scrim fade with the taskbar animation duration to avoid any flickers for
+ // taskbar icons disappearing before hotseat icons show up.
+ float scrimUpperBoundFromSplit = TASKBAR_TO_HOME_DURATION / (float) config.duration;
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
config.setInterpolator(ANIM_SCRIM_FADE,
fromState == OVERVIEW_SPLIT_SELECT
- ? clampToProgress(LINEAR, 0.33f, 1)
+ ? clampToProgress(LINEAR, 0.33f, scrimUpperBoundFromSplit)
: LINEAR);
config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATE);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCELERATE);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 9972e59..7982606 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -23,6 +23,7 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
import static com.android.launcher3.LauncherPrefs.backedUpItem;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
@@ -734,7 +735,8 @@
final int action = event.getActionMasked();
// Note this will create a new consumer every mouse click, as after ACTION_UP from the click
// an ACTION_HOVER_ENTER will fire as well.
- boolean isHoverActionWithoutConsumer = isHoverActionWithoutConsumer(event);
+ boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
+ && isHoverActionWithoutConsumer(event);
CompoundString reasonString = action == ACTION_DOWN
? new CompoundString("onMotionEvent: ") : CompoundString.NO_OP;
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
diff --git a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
index c1fa2f3..6544ba7 100644
--- a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
+++ b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util
+import android.animation.TimeInterpolator
import android.animation.ValueAnimator
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.graphics.Rect
@@ -30,7 +31,13 @@
import com.android.wm.shell.util.TransitionUtil
/** Remote animation which slides the opening targets in and the closing targets out */
-class SlideInRemoteTransition(val isRtl: Boolean) : IRemoteTransition.Stub() {
+class SlideInRemoteTransition(
+ private val isRtl: Boolean,
+ private val pageSpacing: Int,
+ private val cornerRadius: Float,
+ private val interpolator: TimeInterpolator,
+) : IRemoteTransition.Stub() {
+ private val animationDurationMs = 500L
override fun mergeAnimation(
iBinder: IBinder,
@@ -54,6 +61,8 @@
finishCB: IRemoteTransitionFinishedCallback
) {
val anim = ValueAnimator.ofFloat(0f, 1f)
+ anim.interpolator = interpolator
+ anim.duration = animationDurationMs
val closingStartBounds: HashMap<SurfaceControl, Rect> = HashMap()
val openingEndBounds: HashMap<SurfaceControl, Rect> = HashMap()
@@ -67,9 +76,11 @@
}
if (TransitionUtil.isClosingType(chg.mode)) {
closingStartBounds[leash] = chg.startAbsBounds
+ startT.setCrop(leash, chg.startAbsBounds).setCornerRadius(leash, cornerRadius)
}
if (TransitionUtil.isOpeningType(chg.mode)) {
openingEndBounds[leash] = chg.endAbsBounds
+ startT.setCrop(leash, chg.endAbsBounds).setCornerRadius(leash, cornerRadius)
}
}
startT.apply()
@@ -80,7 +91,7 @@
// Translate the surface from its original position on-screen to off-screen on the
// right (or left in RTL)
val startBounds = closingStartBounds[it]
- val targetX = (if (isRtl) -1 else 1) * startBounds!!.right
+ val targetX = (if (isRtl) -1 else 1) * (startBounds!!.right + pageSpacing)
t.setPosition(it, anim.animatedValue as Float * targetX, 0f)
}
openingEndBounds.keys.forEach {
@@ -90,7 +101,7 @@
// Translate the surface from off-screen on the left (or left in RTL) to its final
// position on-screen
val endBounds = openingEndBounds[it]
- val targetX = (if (isRtl) -1 else 1) * endBounds!!.right
+ val targetX = (if (isRtl) -1 else 1) * (endBounds!!.right + pageSpacing)
t.setPosition(it, (1f - anim.animatedValue as Float) * -targetX, 0f)
}
t.apply()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index b165876..a71d74a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
import android.graphics.Rect;
@@ -24,6 +26,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
@@ -48,6 +51,7 @@
}
@Test
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/320490387
@NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
// Start off in light mode.
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e54539f..754f0cb 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,6 +42,7 @@
<attr name="overviewScrimColor" format="color" />
<attr name="popupNotificationDotColor" format="color" />
<attr name="notificationDotColor" format="color" />
+ <attr name="focusOutlineColor" format="color" />
<attr name="folderPaginationColor" format="color" />
<attr name="folderPreviewColor" format="color" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index db9631a..6a484d7 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,7 +25,6 @@
<color name="uninstall_target_hover_tint">#FFF0592B</color>
<color name="focused_background">#80c6c5c5</color>
- <color name="focus_outline_color">@color/material_color_on_secondary_container</color>
<color name="default_shadow_color_no_alpha">#FF000000</color>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 36991b1..b83be35 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -52,6 +52,7 @@
<item name="workspaceAmbientShadowColor">#40000000</item>
<item name="workspaceKeyShadowColor">#89000000</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
+ <item name="focusOutlineColor">@color/material_color_on_secondary_container</item>
<item name="folderPaginationColor">@color/folder_pagination_color_light</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
<item name="folderBackgroundColor">@color/folder_background_light</item>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 834ba04..e9545c8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -81,6 +81,9 @@
private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
private static final float MIN_WIDGET_PADDING_DP = 6f;
+ // Minimum aspect ratio beyond which an extra top padding may be applied to a bottom sheet.
+ private static final float MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING = 1.5f;
+
public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
public static final Consumer<DeviceProfile> DEFAULT_DIMENSION_PROVIDER = dp -> {
@@ -414,8 +417,14 @@
gridVisualizationPaddingY = res.getDimensionPixelSize(
R.dimen.grid_visualization_vertical_cell_spacing);
+ // Tablet portrait mode uses a single pane widget picker and extra padding may be applied on
+ // top to avoid making it look too elongated.
+ final boolean applyExtraTopPadding = isTablet
+ && !isLandscape
+ && (aspectRatio > MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING);
bottomSheetTopPadding = mInsets.top // statusbar height
- + res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
+ + (applyExtraTopPadding ? res.getDimensionPixelSize(
+ R.dimen.bottom_sheet_extra_top_padding) : 0)
+ (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ad764e3..55438fe 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -260,7 +260,7 @@
mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
if (Flags.enablePrivateSpace()) {
mPrivateSpaceHeaderViewController =
- new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+ new PrivateSpaceHeaderViewController(this, mPrivateProfileManager);
}
mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
@@ -980,6 +980,11 @@
return mWorkManager;
}
+ /** Returns whether Private Profile has been setup. */
+ public boolean hasPrivateProfile() {
+ return mHasPrivateApps;
+ }
+
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 35c07c3..ad875e0 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -325,10 +325,6 @@
mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
position++;
addAppsWithSections(mPrivateApps, position);
- if (mActivityContext.getAppsView() != null) {
- mActivityContext.getAppsView().getActiveRecyclerView()
- .scrollToBottomWithMotion();
- }
break;
}
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index c99b69f..aee511c 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
@@ -34,7 +35,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BuildConfig;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
@@ -59,11 +59,11 @@
private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER;
private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key";
private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
- private static final int ANIMATION_DURATION = 2000;
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
private boolean mPrivateSpaceSettingsAvailable;
+ private Runnable mUnlockRunnable;
public PrivateProfileManager(UserManager userManager,
ActivityAllAppsContainerView<?> allApps,
@@ -115,9 +115,17 @@
mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
}
- /** Disables quiet mode for Private Space User Profile. */
- public void unlockPrivateProfile() {
+ /**
+ * Disables quiet mode for Private Space User Profile.
+ * The runnable passed will be executed in the {@link #reset()} method,
+ * when Launcher receives update about profile availability.
+ * The runnable passed is only executed once, and reset after execution.
+ * In case the method is called again, before the previously set runnable was executed,
+ * the runnable will be updated.
+ */
+ public void unlockPrivateProfile(Runnable runnable) {
enableQuietMode(false);
+ mUnlockRunnable = runnable;
}
/** Enables quiet mode for Private Space User Profile. */
@@ -133,11 +141,15 @@
/** Resets the current state of Private Profile, w.r.t. to Launcher. */
public void reset() {
+ int previousState = getCurrentState();
boolean isEnabled = !mAllApps.getAppsStore()
.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
setCurrentState(updatedState);
resetPrivateSpaceDecorator(updatedState);
+ if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
+ applyUnlockRunnable();
+ }
}
/** Opens the Private Space Settings Entry Point. */
@@ -182,13 +194,6 @@
}
// Add Private Space Decorator to the Recycler view.
mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
- if (Flags.privateSpaceAnimation() && mAllApps.getActiveRecyclerView()
- == mainAdapterHolder.mRecyclerView) {
- RecyclerViewAnimationController recyclerViewAnimationController =
- new RecyclerViewAnimationController(mAllApps);
- recyclerViewAnimationController.animateToState(true /* expand */,
- ANIMATION_DURATION, () -> {});
- }
} else {
// Remove Private Space Decorator from the Recycler view.
if (mPrivateAppsSectionDecorator != null) {
@@ -202,6 +207,18 @@
setQuietMode(enable);
}
+ void applyUnlockRunnable() {
+ if (mUnlockRunnable != null) {
+ // reset the runnable to prevent re-execution.
+ MAIN_EXECUTOR.post(mUnlockRunnable);
+ mUnlockRunnable = null;
+ }
+ }
+
+ private boolean transitioningFromLockedToUnlocked(int previousState, int updatedState) {
+ return previousState == STATE_DISABLED && updatedState == STATE_ENABLED;
+ }
+
@Override
public Predicate<UserHandle> getUserMatcher() {
return mPrivateProfileMatcher;
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index 568ce32..bc3269d 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED;
import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION;
@@ -28,6 +29,7 @@
import android.widget.ImageView;
import android.widget.RelativeLayout;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
@@ -36,9 +38,13 @@
* {@link UserProfileState}
*/
public class PrivateSpaceHeaderViewController {
+ private static final int ANIMATION_DURATION = 2000;
+ private final ActivityAllAppsContainerView mAllApps;
private final PrivateProfileManager mPrivateProfileManager;
- public PrivateSpaceHeaderViewController(PrivateProfileManager privateProfileManager) {
+ public PrivateSpaceHeaderViewController(ActivityAllAppsContainerView allApps,
+ PrivateProfileManager privateProfileManager) {
+ this.mAllApps = allApps;
this.mPrivateProfileManager = privateProfileManager;
}
@@ -77,7 +83,8 @@
quietModeButton.setOnClickListener(
view -> {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
- mPrivateProfileManager.unlockPrivateProfile();
+ mPrivateProfileManager.unlockPrivateProfile((this::
+ onPrivateProfileUnlocked));
});
}
default -> quietModeButton.setVisibility(View.GONE);
@@ -106,6 +113,21 @@
}
}
+ private void onPrivateProfileUnlocked() {
+ // If we are on main adapter view, we apply the PS Container expansion animation and
+ // then scroll down to load the entire container, making animation visible.
+ ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder =
+ (ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
+ if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
+ && mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
+ RecyclerViewAnimationController recyclerViewAnimationController =
+ new RecyclerViewAnimationController(mAllApps);
+ recyclerViewAnimationController.animateToState(true /* expand */,
+ ANIMATION_DURATION, () -> {});
+ mAllApps.getActiveRecyclerView().scrollToBottomWithMotion();
+ }
+ }
+
PrivateProfileManager getPrivateProfileManager() {
return mPrivateProfileManager;
}
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 6bef725..8894f45 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -22,7 +22,6 @@
import android.os.UserManager;
import androidx.annotation.IntDef;
-import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
@@ -89,7 +88,6 @@
}
/** Returns current state for the profile type. */
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public int getCurrentState() {
return mCurrentState;
}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index 1a7d797..c36f455 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Flags;
import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
/**
* A helper class to draw background of a focused view.
@@ -30,8 +31,9 @@
implements OnFocusChangeListener {
public FocusIndicatorHelper(View container) {
- super(container, container.getResources().getColor(Flags.enableFocusOutline()
- ? R.color.focus_outline_color : R.color.focused_background));
+ super(container, Flags.enableFocusOutline() ? Themes.getAttrColor(container.getContext(),
+ R.attr.focusOutlineColor)
+ : container.getResources().getColor(R.color.focused_background));
}
@Override
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0dc0d02..7cc33cf 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -704,6 +704,9 @@
@UiEvent(doc = "User tapped on install to private space system shortcut.")
LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP(1565),
+ @UiEvent(doc = "User attempted to create split screen with a widget")
+ LAUNCHER_SPLIT_WIDGET_ATTEMPT(1604)
+
// ADD MORE
;
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 2f16065..4c5bfd8 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -70,6 +70,34 @@
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
/**
+ * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All
+ * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall
+ * list of {@link WidgetsListBaseEntry}s is not sorted.
+ *
+ * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
+ */
+ public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
+ Context context,
+ Predicate<WidgetItem> widgetItemFilter) {
+ ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
+ AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
+
+ for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
+ PackageItemInfo pkgItem = entry.getKey();
+ List<WidgetItem> widgetItems = entry.getValue()
+ .stream()
+ .filter(widgetItemFilter).toList();
+ if (!widgetItems.isEmpty()) {
+ String sectionName = (pkgItem.title == null) ? "" :
+ indexer.computeSectionName(pkgItem.title);
+ result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems));
+ result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
+ }
+ }
+ return result;
+ }
+
+ /**
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
* are sorted (based on label and user), but the overall list of
* {@link WidgetsListBaseEntry}s is not sorted.
@@ -77,18 +105,8 @@
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
- ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
- AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
-
- for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
- PackageItemInfo pkgItem = entry.getKey();
- List<WidgetItem> widgetItems = entry.getValue();
- String sectionName = (pkgItem.title == null) ? "" :
- indexer.computeSectionName(pkgItem.title);
- result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems));
- result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
- }
- return result;
+ // return all items
+ return getFilteredWidgetsListForPicker(context, /*widgetItemFilter=*/ item -> true);
}
/** Returns a mapping of packages to their widgets without static shortcuts. */
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 79d00c9..24f9acd 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
@@ -81,6 +82,8 @@
@Mock
private PackageManager mPackageManager;
+ private boolean mRunnableCalled = false;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -110,7 +113,7 @@
public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
- mPrivateProfileManager.unlockPrivateProfile();
+ mPrivateProfileManager.unlockPrivateProfile(() -> {});
awaitTasksCompleted();
Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -133,6 +136,23 @@
}
@Test
+ public void transitioningToUnlocked_resetCallsPendingRunnable() throws Exception {
+ PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+ doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
+ .thenReturn(false);
+ when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
+ mRunnableCalled = false;
+
+ privateProfileManager.unlockPrivateProfile(this::testRunnable);
+ privateProfileManager.reset();
+
+ awaitTasksCompleted();
+ Mockito.verify(privateProfileManager).applyUnlockRunnable();
+ assertTrue(mRunnableCalled);
+ }
+
+ @Test
public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() {
Intent expectedIntent = new Intent(SAFETY_CENTER_INTENT);
expectedIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
@@ -150,4 +170,8 @@
private static void awaitTasksCompleted() throws Exception {
UI_HELPER_EXECUTOR.submit(() -> null).get();
}
+
+ private void testRunnable() {
+ mRunnableCalled = true;
+ }
}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
index bc09cdd..92fff49 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
@@ -64,13 +64,16 @@
private RelativeLayout mPsHeaderLayout;
@Mock
private PrivateProfileManager mPrivateProfileManager;
+ @Mock
+ private ActivityAllAppsContainerView mAllApps;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = new ActivityContextWrapper(getApplicationContext());
mLayoutInflater = LayoutInflater.from(getApplicationContext());
- mPsHeaderViewController = new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+ mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
+ mPrivateProfileManager);
mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header,
null);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 867a1a8..156568b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -42,7 +42,8 @@
* @param appName app icon to look for
*/
static BySelector getAppIconSelector(String appName) {
- return By.clazz(TextView.class).text(makeMultilinePattern(appName));
+ // focusable=true to avoid matching folder labels
+ return By.clazz(TextView.class).text(makeMultilinePattern(appName)).focusable(true);
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 28e2590..ed47334 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,8 +56,8 @@
*/
public LaunchedAppState launch(String expectedPackageName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "want to launch an app from " + launchableType())) {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(String.format(
+ "want to launch an app (%s) from %s", expectedPackageName, launchableType()))) {
LauncherInstrumentation.log("Launchable.launch before click "
+ mObject.getVisibleCenter() + " in "
+ mLauncher.getVisibleBounds(mObject));