Merge "Always run animateClose when closing TaskMenuView" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index d379132..25db4d7 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -83,6 +83,9 @@
namespace: "launcher"
description: "Enables full width two pane widget picker for tablets in landscape and portrait"
bug: "315055849"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -164,6 +167,13 @@
}
flag {
+ name: "enable_add_app_widget_via_config_activity_v2"
+ namespace: "launcher"
+ description: "When adding app widget through config activity, directly add it to workspace to reduce flicker"
+ bug: "284236964"
+}
+
+flag {
name: "use_activity_overlay"
namespace: "launcher"
description: "Use an activity for home screen overlay"
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 2953c8e..e75812a 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -79,7 +79,7 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
- android:screenOrientation="unspecified"
+ android:screenOrientation="behind"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 37e5309..50e8d0e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -176,6 +176,7 @@
/**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
+ @Override
public void onLauncherVisibilityChanged(boolean isVisible) {
onLauncherVisibilityChanged(isVisible, false /* fromInit */);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 916b1e6..144c0c2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -53,6 +53,7 @@
private boolean mHasSprungOnceThisGesture;
private @Nullable ValueAnimator mSpringBounce;
+ private boolean mGestureInProgress;
private boolean mGestureEnded;
private boolean mAnimationToHomeRunning;
@@ -155,7 +156,12 @@
/**
* Returns an animation to reset the taskbar translation to {@code 0}.
*/
- public ObjectAnimator createAnimToResetTranslation(long duration) {
+ public ValueAnimator createAnimToResetTranslation(long duration) {
+ if (mGestureInProgress) {
+ // Return an empty animator as the translation will reset itself after gesture ends.
+ return ValueAnimator.ofFloat(0).setDuration(duration);
+ }
+
ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
animator.setInterpolator(Interpolators.LINEAR);
animator.setDuration(duration);
@@ -192,6 +198,7 @@
mAnimationToHomeRunning = false;
cancelSpringIfExists();
reset();
+ mGestureInProgress = true;
}
/**
* Called when there is movement to move the taskbar.
@@ -215,6 +222,7 @@
mGestureEnded = true;
startSpring();
}
+ mGestureInProgress = false;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index bb2ac73..8a26054 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -358,4 +358,13 @@
/** Adjusts the hotseat for the bubble bar. */
public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {}
+
+ /**
+ * Adjusts the taskbar based on the visibility of the launcher.
+ * @param isVisible True if launcher is visible, false otherwise.
+ */
+ public void onLauncherVisibilityChanged(boolean isVisible) {
+ mControllers.taskbarStashController.updateStateForFlag(FLAG_IN_APP, !isVisible);
+ mControllers.taskbarStashController.applyState();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 3767cce..577eba6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,6 +20,7 @@
import android.content.Context;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -27,6 +28,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
/**
* Definition for AllApps state
@@ -49,6 +51,24 @@
}
@Override
+ public void onBackPressed(Launcher launcher) {
+ InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
+ Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ super.onBackPressed(launcher);
+ }
+
+ @Override
+ protected void onBackPressCompleted(boolean success) {
+ if (success) {
+ // Animation was successful.
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ } else {
+ // Animation was canceled.
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ }
+ }
+
+ @Override
public String getDescription(Launcher launcher) {
return launcher.getAppsView().getDescription();
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index c2cd11c..dc8ba84 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -68,6 +68,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
@@ -643,6 +644,21 @@
recentsView.post(() -> {
stateManager.moveToRestState();
stateManager.reapplyState();
+
+ // We may have notified launcher is not visible so that taskbar can
+ // stash immediately. Now that the animation is over, we can update
+ // that launcher is still visible.
+ TaskbarUIController controller = recentsView.getSizeStrategy()
+ .getTaskbarController();
+ if (controller != null) {
+ boolean launcherVisible = true;
+ for (RemoteAnimationTarget target : appTargets) {
+ launcherVisible &= target.isTranslucent;
+ }
+ if (launcherVisible) {
+ controller.onLauncherVisibilityChanged(true);
+ }
+ }
});
});
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2aad327..f9486bd 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -47,6 +47,7 @@
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.wm.shell.Flags.enableBubblesLongPressNavHandle;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
@@ -1006,6 +1007,17 @@
mOverviewCommandHelper);
}
}
+ if (enableBubblesLongPressNavHandle()) {
+ // Create bubbles input consumer before NavHandleLongPressInputConsumer.
+ // This allows for nav handle to fall back to bubbles.
+ if (mDeviceState.isBubblesExpanded()) {
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("bubbles expanded, trying to use default input consumer");
+ // Bubbles can handle home gesture itself.
+ base = getDefaultInputConsumer(reasonString);
+ }
+ }
NavHandle navHandle = tac != null ? tac.getNavHandle()
: SystemUiProxy.INSTANCE.get(this);
@@ -1023,12 +1035,15 @@
mDeviceState, navHandle);
}
- if (mDeviceState.isBubblesExpanded()) {
- reasonString = newCompoundString(reasonPrefix)
- .append(SUBSTRING_PREFIX)
- .append("bubbles expanded, trying to use default input consumer");
- // Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(reasonString);
+ if (!enableBubblesLongPressNavHandle()) {
+ // Continue overriding nav handle input consumer with bubbles
+ if (mDeviceState.isBubblesExpanded()) {
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("bubbles expanded, trying to use default input consumer");
+ // Bubbles can handle home gesture itself.
+ base = getDefaultInputConsumer(reasonString);
+ }
}
if (mDeviceState.isSystemUiDialogShowing()) {
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
index 02a49a3..c327166 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -171,17 +171,19 @@
assert(layoutter is PhoneSeascapeNavLayoutter)
}
- @Test(expected = IllegalStateException::class)
- fun noValidLayoutForPhoneGestureNav() {
+ @Test
+ fun getTaskbarPhoneGestureNavLayoutter() {
assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION)
mockDeviceProfile.isTaskbarPresent = false
- getLayoutter(
- isKidsMode = false,
- isInSetup = false,
- isThreeButtonNav = false,
- phoneMode = true,
- surfaceRotation = surfaceRotation
- )
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(
+ isKidsMode = false,
+ isInSetup = false,
+ isThreeButtonNav = false,
+ phoneMode = true,
+ surfaceRotation = surfaceRotation
+ )
+ assert(layoutter is PhoneGestureLayoutter)
}
private fun setDeviceProfileLandscape() {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 39d6f03..213f58f 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -252,6 +252,7 @@
// b/143488140
//@NavigationModeSwitch
@Test
+ @ScreenRecordRule.ScreenRecord // b/321775748
public void testOverview() {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/res/drawable/ic_lock.xml b/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..055e6b4
--- /dev/null
+++ b/res/drawable/ic_lock.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="?attr/materialColorOnPrimaryFixed"
+ android:pathData="M263.72,864Q234,864 213,842.85Q192,821.7 192,792L192,408Q192,378.3 213.15,357.15Q234.3,336 264,336L288,336L288,240Q288,160.32 344.23,104.16Q400.45,48 480.23,48Q560,48 616,104.16Q672,160.32 672,240L672,336L696,336Q725.7,336 746.85,357.15Q768,378.3 768,408L768,792Q768,821.7 746.84,842.85Q725.68,864 695.96,864L263.72,864ZM264,792L696,792Q696,792 696,792Q696,792 696,792L696,408Q696,408 696,408Q696,408 696,408L264,408Q264,408 264,408Q264,408 264,408L264,792Q264,792 264,792Q264,792 264,792ZM480.21,672Q510,672 531,650.79Q552,629.58 552,599.79Q552,570 530.79,549Q509.58,528 479.79,528Q450,528 429,549.21Q408,570.42 408,600.21Q408,630 429.21,651Q450.42,672 480.21,672ZM360,336L600,336L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,336ZM264,792Q264,792 264,792Q264,792 264,792L264,408Q264,408 264,408Q264,408 264,408L264,408Q264,408 264,408Q264,408 264,408L264,792Q264,792 264,792Q264,792 264,792L264,792Z"/>
+</vector>
diff --git a/res/drawable/bg_ps_settings_button.xml b/res/drawable/ic_ps_settings.xml
similarity index 93%
rename from res/drawable/bg_ps_settings_button.xml
rename to res/drawable/ic_ps_settings.xml
index c06e0c0..47edeb8 100644
--- a/res/drawable/bg_ps_settings_button.xml
+++ b/res/drawable/ic_ps_settings.xml
@@ -15,13 +15,10 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/ps_button_height"
+ android:width="@dimen/ps_button_width"
android:height="@dimen/ps_button_height"
android:viewportWidth="40"
android:viewportHeight="40">
- <path
- android:pathData="M20,0L20,0A20,20 0,0 1,40 20L40,20A20,20 0,0 1,20 40L20,40A20,20 0,0 1,0 20L0,20A20,20 0,0 1,20 0z"
- android:fillColor="?attr/materialColorSurfaceBright"/>
<group>
<clip-path
android:pathData="M10,10h20v20h-20z"/>
diff --git a/res/drawable/ps_lock_background.xml b/res/drawable/ps_lock_background.xml
new file mode 100644
index 0000000..b81c23f
--- /dev/null
+++ b/res/drawable/ps_lock_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/ps_lock_corner_radius" />
+ <solid android:color="?attr/materialColorPrimaryFixedDim" />
+ <padding
+ android:left="@dimen/ps_lock_button_background_padding"
+ android:right="@dimen/ps_lock_button_background_padding" />
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/ps_settings_background.xml b/res/drawable/ps_settings_background.xml
new file mode 100644
index 0000000..b0c6b5b
--- /dev/null
+++ b/res/drawable/ps_settings_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/ps_lock_corner_radius" />
+ <solid android:color="?attr/materialColorSurfaceBright" />
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index 24e290d..0b0af87 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -18,6 +18,7 @@
<RelativeLayout
android:id="@+id/ps_header_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/ps_header_height"
android:background="@drawable/bg_ps_header"
@@ -25,27 +26,53 @@
android:gravity="center_vertical"
android:orientation="horizontal">
- <ImageButton
- android:id="@+id/ps_lock_unlock_button"
+ <LinearLayout
+ android:id="@+id/settingsAndLockGroup"
android:layout_width="wrap_content"
- android:layout_height="@dimen/ps_header_image_height"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:background="@android:color/transparent"
- android:layout_marginEnd="@dimen/ps_header_layout_margin"
- android:contentDescription="@string/ps_container_lock_unlock_button" />
-
- <ImageButton
- android:id="@+id/ps_settings_button"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/ps_header_image_height"
- android:layout_toStartOf="@+id/ps_lock_unlock_button"
- android:layout_centerVertical="true"
- android:background="@android:color/transparent"
- android:layout_marginEnd="@dimen/ps_header_settings_icon_margin_end"
- android:src="@drawable/bg_ps_settings_button"
- android:contentDescription="@string/ps_container_settings" />
-
+ android:animateLayoutChanges="true">
+ <ImageButton
+ android:id="@+id/ps_settings_button"
+ android:layout_width="@dimen/ps_header_image_height"
+ android:layout_height="@dimen/ps_header_image_height"
+ android:background="@drawable/ps_settings_background"
+ android:layout_marginEnd="@dimen/ps_header_settings_icon_margin_end"
+ android:src="@drawable/ic_ps_settings"
+ android:contentDescription="@string/ps_container_settings" />
+ <LinearLayout
+ android:id="@+id/ps_lock_unlock_button"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/ps_header_image_height"
+ android:background="@drawable/ps_lock_background"
+ android:gravity="center_vertical"
+ android:layout_marginEnd="@dimen/ps_header_layout_margin"
+ android:contentDescription="@string/ps_container_lock_unlock_button">
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="@dimen/ps_lock_icon_size"
+ android:layout_height="@dimen/ps_lock_icon_size"
+ android:layout_marginTop="@dimen/ps_lock_icon_margin_top"
+ android:layout_marginBottom="@dimen/ps_lock_icon_margin_bottom"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_lock"
+ app:tint="@color/material_color_primary_fixed_dim"
+ android:scaleType="center"/>
+ <TextView
+ android:id="@+id/lock_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/ps_lock_icon_text_margin_start_expanded"
+ android:layout_marginEnd="@dimen/ps_lock_icon_text_margin_end_expanded"
+ android:textColor="@color/material_color_on_primary_fixed"
+ android:textSize="14sp"
+ android:text="@string/ps_container_lock_title"
+ android:visibility="gone"
+ style="@style/TextHeadline"/>
+ </LinearLayout>
+ </LinearLayout>
<ImageView
android:id="@+id/ps_transition_image"
android:layout_width="wrap_content"
@@ -63,6 +90,7 @@
android:layout_height="@dimen/ps_header_text_height"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
+ android:layout_toStartOf="@+id/settingsAndLockGroup"
android:gravity="center_vertical"
android:layout_marginStart="@dimen/ps_header_layout_margin"
android:text="@string/ps_container_title"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c187000..c7190b6 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -476,15 +476,22 @@
<dimen name="ps_container_corner_radius">24dp</dimen>
<dimen name="ps_header_height">64dp</dimen>
<dimen name="ps_header_relative_layout_height">48dp</dimen>
- <dimen name="ps_header_image_height">36dp</dimen>
+ <dimen name="ps_header_image_height">48dp</dimen>
<dimen name="ps_header_text_height">24dp</dimen>
<dimen name="ps_header_layout_margin">16dp</dimen>
<dimen name="ps_header_settings_icon_margin_end">8dp</dimen>
<dimen name="ps_header_text_size">16sp</dimen>
- <dimen name="ps_button_height">36dp</dimen>
- <dimen name="ps_button_width">36dp</dimen>
+ <dimen name="ps_button_height">40dp</dimen>
+ <dimen name="ps_button_width">40dp</dimen>
<dimen name="ps_lock_button_width">89dp</dimen>
<dimen name="ps_app_divider_padding">16dp</dimen>
+ <dimen name="ps_lock_corner_radius">20dp</dimen>
+ <dimen name="ps_lock_icon_size">20dp</dimen>
+ <dimen name="ps_lock_icon_margin_top">10dp</dimen>
+ <dimen name="ps_lock_icon_margin_bottom">10dp</dimen>
+ <dimen name="ps_lock_icon_text_margin_start_expanded">8dp</dimen>
+ <dimen name="ps_lock_icon_text_margin_end_expanded">6dp</dimen>
+ <dimen name="ps_lock_button_background_padding">10dp</dimen>
<!-- WindowManagerProxy -->
<dimen name="max_width_and_height_of_small_display_cutout">136px</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a4e7ec4..31c098c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -465,6 +465,7 @@
<string name="ps_container_settings">Private Space Settings</string>
<!-- Description for Private Space Lock/Unlock button -->
<string name="ps_container_lock_unlock_button">Lock/Unlock Private Space</string>
+ <string name="ps_container_lock_title">Lock</string>
<!-- Description for Private Space Transition button -->
<string name="ps_container_transition">Private Space Transitioning</string>
<!-- Title for Private Space install app icon -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b83be35..401155d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -441,5 +441,6 @@
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/material_color_on_surface</item>
<item name="android:fontFamily">google-sans-text-medium</item>
+ <item name="android:ellipsize">end</item>
</style>
</resources>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 2f0c096..38ddfec 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -294,6 +294,12 @@
mIconLoadRequest.cancel();
mIconLoadRequest = null;
}
+ // Reset any shifty arrangements in case animation is disrupted.
+ setPivotY(0);
+ setAlpha(1);
+ setScaleY(1);
+ setTranslationY(0);
+ setVisibility(VISIBLE);
}
private void cancelDotScaleAnim() {
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 7ec0a89..51c7a05 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -201,7 +201,6 @@
if (mScrollbar != null) {
mScrollbar.reattachThumbToScroll();
}
- // Emphasized interpolators with 500ms duration
smoothScrollBy(0, getAvailableScrollHeight(), Interpolators.EMPHASIZED, duration);
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2471254..07eea32 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
+import static com.android.launcher3.Flags.enableAddAppWidgetViaConfigActivityV2;
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
@@ -116,6 +117,8 @@
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -828,7 +831,7 @@
announceForAccessibility(R.string.item_added_to_workspace);
break;
case REQUEST_CREATE_APPWIDGET:
- completeAddAppWidget(appWidgetId, info, null, null);
+ completeAddAppWidget(appWidgetId, info, null, null, false, null);
break;
case REQUEST_RECONFIGURE_APPWIDGET:
getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
@@ -1015,11 +1018,18 @@
AppWidgetHostView boundWidget = null;
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
- final AppWidgetHostView layout = mAppWidgetHolder.createView(appWidgetId,
- requestArgs.getWidgetHandler().getProviderInfo(this));
+
+ // Now that we are exiting the config activity with RESULT_OK.
+ // If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we can retrieve the
+ // PendingAppWidgetHostView from LauncherWidgetHolder (it was added to
+ // LauncherWidgetHolder when starting the config activity).
+ final AppWidgetHostView layout = enableAddAppWidgetViaConfigActivityV2()
+ ? getWorkspace().getWidgetForAppWidgetId(appWidgetId)
+ : mAppWidgetHolder.createView(appWidgetId,
+ requestArgs.getWidgetHandler().getProviderInfo(this));
boundWidget = layout;
onCompleteRunnable = () -> {
- completeAddAppWidget(appWidgetId, requestArgs, layout, null);
+ completeAddAppWidget(appWidgetId, requestArgs, layout, null, false, null);
if (!isInState(EDIT_MODE)) {
mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
@@ -1449,14 +1459,15 @@
*/
@Thunk
void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
- AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
+ @Nullable AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo,
+ boolean showPendingWidget, @Nullable Bitmap widgetPreviewBitmap) {
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId,
itemInfo.getTargetComponent());
}
- if (hostView == null) {
+ if (hostView == null && !showPendingWidget) {
// Perform actual inflation because we're live
hostView = mAppWidgetHolder.createView(appWidgetId, appWidgetInfo);
}
@@ -1470,39 +1481,71 @@
launcherInfo.minSpanX = itemInfo.minSpanX;
launcherInfo.minSpanY = itemInfo.minSpanY;
launcherInfo.user = appWidgetInfo.getProfile();
+ CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo);
+ if (showPendingWidget) {
+ launcherInfo.restoreStatus = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ PendingAppWidgetHostView pendingAppWidgetHostView =
+ new PendingAppWidgetHostView(this, launcherInfo, appWidgetInfo);
+ pendingAppWidgetHostView.setPreviewBitmap(widgetPreviewBitmap);
+ hostView = pendingAppWidgetHostView;
+ } else if (hostView instanceof PendingAppWidgetHostView) {
+ ((PendingAppWidgetHostView) hostView).setPreviewBitmap(null);
+ // User has selected a widget config and exited the config activity, we can trigger
+ // re-inflation of PendingAppWidgetHostView to replace it with
+ // LauncherAppWidgetHostView in workspace.
+ completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
+
+ // Show resize frame on the newly inflated LauncherAppWidgetHostView.
+ LauncherAppWidgetHostView reInflatedHostView =
+ getWorkspace().getWidgetForAppWidgetId(appWidgetId);
+ showWidgetResizeFrame(
+ reInflatedHostView,
+ (LauncherAppWidgetInfo) reInflatedHostView.getTag(),
+ presenterPos);
+ return;
+ }
if (itemInfo instanceof PendingAddWidgetInfo) {
launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer;
} else if (itemInfo instanceof PendingRequestArgs) {
launcherInfo.sourceContainer =
((PendingRequestArgs) itemInfo).getWidgetSourceContainer();
}
- CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo);
getModelWriter().addItemToDatabase(launcherInfo,
itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY);
hostView.setVisibility(View.VISIBLE);
mItemInflater.prepareAppWidget(hostView, launcherInfo);
- mWorkspace.addInScreen(hostView, launcherInfo);
+ if (!enableAddAppWidgetViaConfigActivityV2() || hostView.getParent() == null) {
+ mWorkspace.addInScreen(hostView, launcherInfo);
+ }
announceForAccessibility(R.string.item_added_to_workspace);
// Show the widget resize frame.
if (hostView instanceof LauncherAppWidgetHostView) {
final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView;
- CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId);
- if (mStateManager.getState() == NORMAL) {
- AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
- } else {
- mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE)
- && finalState == NORMAL) {
- AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
- mStateManager.removeStateListener(this);
- }
+ showWidgetResizeFrame(launcherHostView, launcherInfo, presenterPos);
+ }
+ }
+
+ /** Show widget resize frame. */
+ private void showWidgetResizeFrame(
+ LauncherAppWidgetHostView launcherHostView,
+ LauncherAppWidgetInfo launcherInfo,
+ CellPos presenterPos) {
+ CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId);
+ if (mStateManager.getState() == NORMAL) {
+ AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
+ } else {
+ mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE)
+ && finalState == NORMAL) {
+ AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
+ mStateManager.removeStateListener(this);
}
- });
- }
+ }
+ });
}
}
@@ -1759,19 +1802,39 @@
addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0);
}
+ /**
+ * If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we always add widget
+ * host view to workspace, otherwise we only add widget to host view if config activity is
+ * not started.
+ */
void addAppWidgetImpl(int appWidgetId, ItemInfo info,
AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) {
- if (!addFlowHandler.startConfigActivity(this, appWidgetId, info,
- REQUEST_CREATE_APPWIDGET)) {
- // If the configuration flow was not started, add the widget
+ final boolean isActivityStarted = addFlowHandler.startConfigActivity(
+ this, appWidgetId, info, REQUEST_CREATE_APPWIDGET);
- // Exit spring loaded mode if necessary after adding the widget
- Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null
- : () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
- completeAddAppWidget(appWidgetId, info, boundWidget,
- addFlowHandler.getProviderInfo(this));
- mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
+ if (!enableAddAppWidgetViaConfigActivityV2() && isActivityStarted) {
+ return;
}
+
+ // If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled and config activity is
+ // started, we should remove the dropped AppWidgetHostView from drag layer and extract the
+ // Bitmap that shows the preview. Then pass the Bitmap to completeAddAppWidget() to create
+ // a PendingWidgetHostView.
+ Bitmap widgetPreviewBitmap = null;
+ if (isActivityStarted) {
+ DragView dropView = getDragLayer().clearAnimatedView();
+ if (dropView != null && dropView.containsAppWidgetHostView()) {
+ widgetPreviewBitmap = getBitmapFromView(dropView.getContentView());
+ }
+ }
+
+ // Exit spring loaded mode if necessary after adding the widget
+ Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null
+ : () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+ completeAddAppWidget(appWidgetId, info, boundWidget,
+ addFlowHandler.getProviderInfo(this), addFlowHandler.needsConfigure(),
+ widgetPreviewBitmap);
+ mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
}
public void addPendingItem(PendingAddItemInfo info, int container, int screenId,
@@ -2366,6 +2429,18 @@
return null;
}
+ /** Convert a {@link View} to {@link Bitmap}. */
+ private static Bitmap getBitmapFromView(@Nullable View view) {
+ if (view == null) {
+ return null;
+ }
+ Bitmap returnedBitmap =
+ Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(returnedBitmap);
+ view.draw(canvas);
+ return returnedBitmap;
+ }
+
/**
* Returns the first view matching the operator in the given ViewGroups, or null if none.
* Forward iteration matters.
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index d2ea7cc..6e66c14 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -17,6 +17,7 @@
import static com.android.app.animation.Interpolators.ACCELERATE_2;
import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
@@ -427,11 +428,20 @@
if (this != NORMAL) {
StateManager<LauncherState> lsm = launcher.getStateManager();
LauncherState lastState = lsm.getLastState();
- lsm.goToState(lastState);
+ lsm.goToState(lastState, forEndCallback(this::onBackPressCompleted));
}
}
/**
+ * To be called if back press is completed in a launcher state.
+ *
+ * @param success whether back press animation was successful or canceled.
+ */
+ protected void onBackPressCompleted(boolean success) {
+ // Do nothing. To be overridden by child class.
+ }
+
+ /**
* Find {@link StateManager} and target {@link LauncherState} to handle back progress in
* predictive back gesture.
*/
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index d460ba8..6168e41 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -30,6 +30,7 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.Executors;
import java.util.Locale;
@@ -51,13 +52,13 @@
@WorkerThread
private static void processIntent(Context context, Intent intent) {
- if (!isEnabled(context)) {
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (!isEnabled(context, user)) {
// User has decided to not add icons on homescreen.
return;
}
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
- UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
if (!PackageInstaller.ACTION_SESSION_COMMITTED.equals(intent.getAction())
|| info == null || user == null) {
// Invalid intent.
@@ -92,7 +93,17 @@
.queueItem(info.getAppPackageName(), user);
}
- public static boolean isEnabled(Context context) {
+ /**
+ * Returns whether adding Installed App Icons to home screen is allowed or not.
+ * Not allowed when:
+ * - User belongs to {@link com.android.launcher3.util.UserIconInfo.TYPE_PRIVATE} or
+ * - Home Settings preference to add App Icons on Home Screen is set as disabled
+ */
+ public static boolean isEnabled(Context context, UserHandle user) {
+ if (Flags.privateSpaceRestrictItemDrag() && user != null
+ && UserCache.getInstance(context).getUserInfo(user).isPrivate()) {
+ return false;
+ }
return LauncherPrefs.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 86f31a1..55416af 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2988,7 +2988,7 @@
}
public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
- final Runnable onCompleteRunnable, int animationType, final View finalView,
+ final Runnable onCompleteRunnable, int animationType, @Nullable final View finalView,
boolean external) {
int[] finalPos = new int[2];
float scaleXY[] = new float[2];
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 28c87b6..27092f6 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -258,6 +258,7 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
+ holder.itemView.setVisibility(View.VISIBLE);
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON: {
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index fcdfaa6..91fcf80 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -16,7 +16,12 @@
package com.android.launcher3.allapps;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
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;
@@ -24,21 +29,37 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
-import android.view.View;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
+import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
+import android.widget.TextView;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.app.animation.Interpolators;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
+import com.android.launcher3.anim.AnimatedPropertySetter;
+import com.android.launcher3.anim.PropertySetter;
+
+import java.util.List;
/**
* Controller which returns views to be added to Private Space Header based upon
* {@link UserProfileState}
*/
public class PrivateSpaceHeaderViewController {
- private static final int ANIMATION_DURATION = 2000;
+ private static final int EXPAND_SCROLL_DURATION = 2000;
+ private static final int EXPAND_COLLAPSE_DURATION = 800;
+ private static final int SETTINGS_OPACITY_DURATION = 160;
private final ActivityAllAppsContainerView mAllApps;
private final PrivateProfileManager mPrivateProfileManager;
@@ -50,10 +71,17 @@
/** Add Private Space Header view elements based upon {@link UserProfileState} */
public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
+ // Set the transition duration for the settings and lock button to animate.
+ ViewGroup settingsAndLockGroup = parent.findViewById(R.id.settingsAndLockGroup);
+ LayoutTransition settingsAndLockTransition = settingsAndLockGroup.getLayoutTransition();
+ settingsAndLockTransition.enableTransitionType(LayoutTransition.CHANGING);
+ settingsAndLockTransition.setDuration(EXPAND_COLLAPSE_DURATION);
+
//Add quietMode image and action for lock/unlock button
- ImageButton quietModeButton = parent.findViewById(R.id.ps_lock_unlock_button);
- assert quietModeButton != null;
- addQuietModeButton(quietModeButton);
+ ViewGroup lockButton =
+ parent.findViewById(R.id.ps_lock_unlock_button);
+ assert lockButton != null;
+ addLockButton(parent, lockButton);
//Trigger lock/unlock action from header.
addHeaderOnClickListener(parent);
@@ -69,74 +97,181 @@
addTransitionImage(transitionView);
}
- private void addQuietModeButton(ImageButton quietModeButton) {
+ /**
+ * Adds the quietModeButton and attach onClickListener for the header to animate different
+ * states when clicked.
+ */
+ private void addLockButton(ViewGroup psHeader, ViewGroup lockButton) {
+ TextView lockText = lockButton.findViewById(R.id.lock_text);
switch (mPrivateProfileManager.getCurrentState()) {
case STATE_ENABLED -> {
- quietModeButton.setVisibility(View.VISIBLE);
- quietModeButton.setImageResource(R.drawable.bg_ps_lock_button);
- quietModeButton.setOnClickListener(view -> lockAction());
+ lockText.setVisibility(VISIBLE);
+ lockButton.setVisibility(VISIBLE);
+ lockButton.setOnClickListener(view -> lockAction(psHeader));
}
case STATE_DISABLED -> {
- quietModeButton.setVisibility(View.VISIBLE);
- quietModeButton.setImageResource(R.drawable.bg_ps_unlock_button);
- quietModeButton.setOnClickListener(view -> unLockAction());
+ lockText.setVisibility(GONE);
+ lockButton.setVisibility(VISIBLE);
+ lockButton.setOnClickListener(view -> unlockAction(psHeader));
}
- default -> quietModeButton.setVisibility(View.GONE);
+ default -> lockButton.setVisibility(GONE);
}
}
private void addHeaderOnClickListener(RelativeLayout header) {
if (mPrivateProfileManager.getCurrentState() == STATE_DISABLED) {
- header.setOnClickListener(view -> unLockAction());
+ header.setOnClickListener(view -> unlockAction(header));
} else {
header.setOnClickListener(null);
}
}
- private void unLockAction() {
+ private void unlockAction(ViewGroup psHeader) {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
- mPrivateProfileManager.unlockPrivateProfile((this::onPrivateProfileUnlocked));
+ mPrivateProfileManager.unlockPrivateProfile((() -> onPrivateProfileUnlocked(psHeader)));
}
- private void lockAction() {
+ private void lockAction(ViewGroup psHeader) {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP);
- mPrivateProfileManager.lockPrivateProfile();
+ if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()) {
+ updatePrivateStateAnimator(false, psHeader);
+ } else {
+ mPrivateProfileManager.lockPrivateProfile();
+ }
}
private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
if (mPrivateProfileManager.getCurrentState() == STATE_ENABLED
&& mPrivateProfileManager.isPrivateSpaceSettingsAvailable()) {
- settingsButton.setVisibility(View.VISIBLE);
+ settingsButton.setVisibility(VISIBLE);
+ settingsButton.setAlpha(1f);
settingsButton.setOnClickListener(
view -> {
mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
mPrivateProfileManager.openPrivateSpaceSettings();
});
} else {
- settingsButton.setVisibility(View.GONE);
+ settingsButton.setVisibility(GONE);
}
}
private void addTransitionImage(ImageView transitionImage) {
if (mPrivateProfileManager.getCurrentState() == STATE_TRANSITION) {
- transitionImage.setVisibility(View.VISIBLE);
+ transitionImage.setVisibility(VISIBLE);
} else {
- transitionImage.setVisibility(View.GONE);
+ transitionImage.setVisibility(GONE);
}
}
- private void onPrivateProfileUnlocked() {
+ private void onPrivateProfileUnlocked(ViewGroup header) {
// 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) {
- mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(ANIMATION_DURATION);
+ // Animate the text and settings icon.
+ updatePrivateStateAnimator(true, header);
+ mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(EXPAND_SCROLL_DURATION);
+ }
+ }
+
+ /** Finds the private space header to scroll to and set the private space icons to GONE. */
+ private void collapse() {
+ AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+ for (int i = allAppsRecyclerView.getChildCount() - 1; i > 0; i--) {
+ int adapterPosition = allAppsRecyclerView.getChildAdapterPosition(
+ allAppsRecyclerView.getChildAt(i));
+ List<BaseAllAppsAdapter.AdapterItem> allAppsAdapters = allAppsRecyclerView.getApps()
+ .getAdapterItems();
+ if (adapterPosition < 0 || adapterPosition >= allAppsAdapters.size()) {
+ continue;
+ }
+ // Scroll to the private space header.
+ if (allAppsAdapters.get(adapterPosition).viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ // Note: SmoothScroller is meant to be used once.
+ RecyclerView.SmoothScroller smoothScroller =
+ new LinearSmoothScroller(mAllApps.getContext()) {
+ @Override protected int getVerticalSnapPreference() {
+ return LinearSmoothScroller.SNAP_TO_END;
+ }
+ };
+ smoothScroller.setTargetPosition(adapterPosition);
+ RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
+ if (layoutManager != null) {
+ layoutManager.startSmoothScroll(smoothScroller);
+ }
+ break;
+ }
+ // Make the private space apps gone to "collapse".
+ if (allAppsAdapters.get(adapterPosition).decorationInfo != null) {
+ allAppsRecyclerView.getChildAt(i).setVisibility(GONE);
+ }
}
}
PrivateProfileManager getPrivateProfileManager() {
return mPrivateProfileManager;
}
+
+ /**
+ * Scrolls up to the private space header and animates the collapsing of the text.
+ */
+ private ValueAnimator animateCollapseAnimation(ViewGroup lockButton) {
+ float from = 1;
+ float to = 0;
+ ValueAnimator collapseAnim = ValueAnimator.ofFloat(from, to);
+ collapseAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+ collapseAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // scroll up
+ collapse();
+ // Animate the collapsing of the text.
+ lockButton.findViewById(R.id.lock_text).setVisibility(GONE);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mPrivateProfileManager.lockPrivateProfile();
+ }
+ });
+ return collapseAnim;
+ }
+
+ /**
+ * Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
+ * animation. At the moment, collapsing, setting alpha changes, and animating the text is done
+ * here.
+ */
+ private void updatePrivateStateAnimator(boolean expand, ViewGroup psHeader) {
+ PropertySetter setter = new AnimatedPropertySetter();
+ ViewGroup lockButton = psHeader.findViewById(R.id.ps_lock_unlock_button);
+ ImageButton settingsButton = psHeader.findViewById(R.id.ps_settings_button);
+ updateSettingsGearAlpha(settingsButton, expand, setter);
+ AnimatorSet animatorSet = setter.buildAnim();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Animate the collapsing of the text at the same time while updating lock button.
+ lockButton.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
+ }
+ });
+ // Play the collapsing together of the stateAnimator to avoid being unable to scroll to the
+ // header. Otherwise the smooth scrolling will scroll higher when played with the state
+ // animator.
+ if (!expand) {
+ animatorSet.playTogether(animateCollapseAnimation(lockButton));
+ }
+ animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
+ animatorSet.start();
+ }
+
+ /** Change the settings gear alpha when expanded or collapsed. */
+ private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
+ PropertySetter setter) {
+ float toAlpha = expand ? 1 : 0;
+ setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
+ .setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(0);
+ }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e2902e9..072a96c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -31,6 +31,7 @@
import static com.android.launcher3.uioverrides.flags.FlagsFactory.getReleaseFlag;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
+import android.content.res.Resources;
import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
@@ -222,7 +223,19 @@
TEAMFOOD, "Sends a notification whenever launcher encounters an uncaught exception.");
public static final boolean ENABLE_TASKBAR_NAVBAR_UNIFICATION =
- enableTaskbarNavbarUnification();
+ enableTaskbarNavbarUnification() && !isPhone();
+
+ private static boolean isPhone() {
+ final boolean isPhone;
+ int foldedDeviceStatesId = Resources.getSystem().getIdentifier(
+ "config_foldedDeviceStates", "array", "android");
+ if (foldedDeviceStatesId != 0) {
+ isPhone = Resources.getSystem().getIntArray(foldedDeviceStatesId).length == 0;
+ } else {
+ isPhone = true;
+ }
+ return isPhone;
+ }
// Aconfig migration complete for ENABLE_TASKBAR_NO_RECREATION.
public static final BooleanFlag ENABLE_TASKBAR_NO_RECREATION = getDebugFlag(299193589,
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index f18f900..db693f0 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -42,6 +42,8 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DropTargetBar;
@@ -388,7 +390,13 @@
mDropAnim.start();
}
- public void clearAnimatedView() {
+ /**
+ * Remove the drop view and end the drag animation.
+ *
+ * @return {@link DragView} that is removed.
+ */
+ @Nullable
+ public DragView clearAnimatedView() {
if (mDropAnim != null) {
mDropAnim.cancel();
}
@@ -396,8 +404,10 @@
if (mDropView != null) {
mDragController.onDeferredEndDrag(mDropView);
}
+ DragView ret = mDropView;
mDropView = null;
invalidate();
+ return ret;
}
public View getAnimatedView() {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1f07352..bcee442 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -30,6 +30,7 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -563,6 +564,11 @@
return mContentViewParent;
}
+ /** Return true if {@link mContent} is a {@link AppWidgetHostView}. */
+ public boolean containsAppWidgetHostView() {
+ return mContent instanceof AppWidgetHostView;
+ }
+
private static class SpringFloatValue {
private static final FloatPropertyCompat<SpringFloatValue> VALUE =
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 59f453a..d350879 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -18,7 +18,6 @@
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
@@ -45,6 +44,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -300,7 +300,8 @@
} else {
lai = laiList.get(0);
si.intent = makeLaunchIntent(lai);
- if (enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
+ if (Utilities.enableSupportForArchiving()
+ && lai.getActivityInfo().isArchived) {
si.runtimeStatusFlags |= FLAG_ARCHIVED;
}
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 605ef16..2ec994e 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -211,7 +211,8 @@
*/
@WorkerThread
void tryQueuePromiseAppIcon(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
- if (SessionCommitReceiver.isEnabled(mAppContext)
+ if (sessionInfo != null
+ && SessionCommitReceiver.isEnabled(mAppContext, getUserHandle(sessionInfo))
&& verifySessionInfo(sessionInfo)
&& !promiseIconAddedForId(sessionInfo.getSessionId())) {
FileLog.d(LOG, "Adding package name to install queue: "
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index e6e7133..51bc339 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -159,6 +159,13 @@
/**
* @see #goToState(STATE_TYPE, boolean, AnimatorListener)
*/
+ public void goToState(STATE_TYPE state, AnimatorListener listener) {
+ goToState(state, shouldAnimateStateChange(), listener);
+ }
+
+ /**
+ * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
+ */
public void goToState(STATE_TYPE state, boolean animated) {
goToState(state, animated, 0, null);
}
@@ -168,7 +175,7 @@
*
* @param animated false if the state should change immediately without any animation,
* true otherwise
- * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
+ * @param listener any action to perform at the end of the transition, or null.
*/
public void goToState(STATE_TYPE state, boolean animated, AnimatorListener listener) {
goToState(state, animated, 0, listener);
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 1623ad8..1231cd7 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
@@ -182,6 +183,11 @@
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, mDeviceProfile.isTablet);
return response;
+ case TestProtocol.REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION:
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ ENABLE_TASKBAR_NAVBAR_UNIFICATION);
+ return response;
+
case TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS:
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
mDeviceProfile.numShownAllAppsColumns);
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 92288e1..11d8e97 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,8 +16,6 @@
package com.android.launcher3.util;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
-
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -276,6 +274,6 @@
@SuppressWarnings("NewApi")
private boolean isPackageInstalledOrArchived(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
- enableSupportForArchiving() && info.isArchived);
+ Utilities.enableSupportForArchiving() && info.isArchived);
}
}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 25979c2..adf85c7 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -16,13 +16,20 @@
package com.android.launcher3.widget;
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
@@ -85,8 +92,12 @@
private boolean mDrawableSizeChanged;
private final TextPaint mPaint;
+
+ private final Paint mPreviewPaint;
private Layout mSetupTextLayout;
+ @Nullable private Bitmap mPreviewBitmap;
+
public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
@Nullable LauncherAppWidgetProviderInfo appWidget) {
this(context, info, appWidget,
@@ -116,6 +127,15 @@
mDrawableSizeChanged = true;
}
+ /** Set {@link Bitmap} of widget preview. */
+ public void setPreviewBitmap(@Nullable Bitmap previewBitmap) {
+ if (this.mPreviewBitmap == previewBitmap) {
+ return;
+ }
+ this.mPreviewBitmap = previewBitmap;
+ invalidate();
+ }
+
private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
LauncherAppWidgetProviderInfo appwidget, CharSequence label) {
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
@@ -130,12 +150,17 @@
mPaint.setColor(Themes.getAttrColor(getContext(), android.R.attr.textColorPrimary));
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
-
+ mPreviewPaint = new Paint(ANTI_ALIAS_FLAG | DITHER_FLAG | FILTER_BITMAP_FLAG);
setWillNotDraw(false);
setBackgroundResource(R.drawable.pending_widget_bg);
}
@Override
+ public AppWidgetProviderInfo getAppWidgetInfo() {
+ return mAppwidget;
+ }
+
+ @Override
public void updateAppWidget(RemoteViews remoteViews) {
checkIfRestored();
}
@@ -393,6 +418,11 @@
@Override
protected void onDraw(Canvas canvas) {
+ if (mPreviewBitmap != null
+ && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0) {
+ canvas.drawBitmap(mPreviewBitmap, 0, 0, mPreviewPaint);
+ return;
+ }
if (mCenterDrawable == null) {
// Nothing to draw
return;
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
index 3fff622..bcbe343 100644
--- a/tests/Launcher3Tests.xml
+++ b/tests/Launcher3Tests.xml
@@ -46,5 +46,6 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.launcher3.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
</test>
</configuration>
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 97da7df..4e1e9c8 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -119,6 +119,8 @@
public static final String REQUEST_CLEAR_DATA = "clear-data";
public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
public static final String REQUEST_IS_TABLET = "is-tablet";
+ public static final String REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION =
+ "enable-taskbar-navbar-unification";
public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_CELL_LAYOUT_BOARDER_HEIGHT = "cell-layout-boarder-height";
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e17994c..326802f 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -405,6 +405,11 @@
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ public boolean isTaskbarNavbarUnificationEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION)
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public boolean isTwoPanels() {
return getTestInfo(TestProtocol.REQUEST_IS_TWO_PANELS)
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -845,7 +850,8 @@
}
private String getNavigationButtonResPackage() {
- return isTablet() ? getLauncherPackageName() : SYSTEMUI_PACKAGE;
+ return isTablet() || isTaskbarNavbarUnificationEnabled()
+ ? getLauncherPackageName() : SYSTEMUI_PACKAGE;
}
UiObject2 verifyContainerType(ContainerType containerType) {
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
index 92fff49..490cb47 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
@@ -24,6 +24,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.Mockito.when;
import android.content.Context;
@@ -33,8 +34,11 @@
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,7 +63,6 @@
private static final int PS_TRANSITION_IMAGE_COUNT = 1;
private Context mContext;
- private LayoutInflater mLayoutInflater;
private PrivateSpaceHeaderViewController mPsHeaderViewController;
private RelativeLayout mPsHeaderLayout;
@Mock
@@ -71,16 +74,15 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = new ActivityContextWrapper(getApplicationContext());
- mLayoutInflater = LayoutInflater.from(getApplicationContext());
mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
mPrivateProfileManager);
- mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header,
- null);
+ mPsHeaderLayout = (RelativeLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.private_space_header, null);
}
@Test
public void privateProfileDisabled_psHeaderContainsLockedView() throws Exception {
- Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.bg_ps_unlock_button));
+ Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
@@ -93,11 +95,15 @@
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
- } else if (view.getId() == R.id.ps_lock_unlock_button
- && view instanceof ImageView imageView) {
+ } else if (view.getId() == R.id.settingsAndLockGroup) {
+ ImageView lockIcon = view.findViewById(R.id.lock_icon);
+ assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(unlockButton));
+ assertEquals(View.VISIBLE, lockIcon.getVisibility());
+
+ // Verify textView shouldn't be showing when disabled.
+ TextView lockText = view.findViewById(R.id.lock_text);
+ assertEquals(View.GONE, lockText.getVisibility());
totalLockUnlockButtonView += 1;
- assertEquals(View.VISIBLE, view.getVisibility());
- getBitmap(imageView.getDrawable()).sameAs(unlockButton);
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -108,8 +114,8 @@
@Test
public void privateProfileEnabled_psHeaderContainsUnlockedView() throws Exception {
- Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button));
- Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_settings_button));
+ Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
+ Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.ic_ps_settings));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
@@ -124,16 +130,20 @@
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
- } else if (view.getId() == R.id.ps_lock_unlock_button
- && view instanceof ImageView imageView) {
- totalLockUnlockButtonView += 1;
- assertEquals(View.VISIBLE, view.getVisibility());
- getBitmap(imageView.getDrawable()).sameAs(lockImage);
- } else if (view.getId() == R.id.ps_settings_button
- && view instanceof ImageView imageView) {
+ } else if (view.getId() == R.id.settingsAndLockGroup) {
+ // Look for settings button.
+ ImageButton settingsButton = view.findViewById(R.id.ps_settings_button);
+ assertEquals(View.VISIBLE, settingsButton.getVisibility());
totalSettingsImageView += 1;
- assertEquals(View.VISIBLE, view.getVisibility());
- getBitmap(imageView.getDrawable()).sameAs(settingsImage);
+ assertTrue(getBitmap(settingsButton.getDrawable()).sameAs(settingsImage));
+
+ // Look for lock_icon and lock_text.
+ ImageView lockIcon = view.findViewById(R.id.lock_icon);
+ assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
+ assertEquals(View.VISIBLE, lockIcon.getVisibility());
+ TextView lockText = view.findViewById(R.id.lock_text);
+ assertEquals(View.VISIBLE, lockText.getVisibility());
+ totalLockUnlockButtonView += 1;
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -146,7 +156,7 @@
@Test
public void privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView()
throws Exception {
- Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button));
+ Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
@@ -161,11 +171,18 @@
if (view.getId() == R.id.ps_container_header) {
totalContainerHeaderView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
- } else if (view.getId() == R.id.ps_lock_unlock_button
- && view instanceof ImageView imageView) {
+ } else if (view.getId() == R.id.settingsAndLockGroup) {
+ // Ensure there is no settings button.
+ ImageButton settingsImage = view.findViewById(R.id.ps_settings_button);
+ assertEquals(View.GONE, settingsImage.getVisibility());
+
+ // Check lock icon and lock text is there.
+ ImageView lockIcon = view.findViewById(R.id.lock_icon);
+ assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
+ assertEquals(View.VISIBLE, lockIcon.getVisibility());
+ TextView lockText = view.findViewById(R.id.lock_text);
+ assertEquals(View.VISIBLE, lockText.getVisibility());
totalLockUnlockButtonView += 1;
- assertEquals(View.VISIBLE, view.getVisibility());
- getBitmap(imageView.getDrawable()).sameAs(lockImage);
} else {
assertEquals(View.GONE, view.getVisibility());
}
@@ -194,7 +211,10 @@
&& view instanceof ImageView imageView) {
totalLockUnlockButtonView += 1;
assertEquals(View.VISIBLE, view.getVisibility());
- getBitmap(imageView.getDrawable()).sameAs(transitionImage);
+ assertTrue(getBitmap(imageView.getDrawable()).sameAs(transitionImage));
+ } else if (view.getId() == R.id.settingsAndLockGroup) {
+ LinearLayout lockUnlockButton = view.findViewById(R.id.ps_lock_unlock_button);
+ assertEquals(View.GONE, lockUnlockButton.getVisibility());
} else {
assertEquals(View.GONE, view.getVisibility());
}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 0f23165..848ae82 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -25,7 +25,6 @@
import android.content.Intent;
import android.platform.test.annotations.PlatinumTest;
-import android.platform.test.rule.ScreenRecordRule;
import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -35,6 +34,7 @@
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Before;
import org.junit.Test;