Merge "Clean up work profile" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index fc99880..cb695df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,7 +32,7 @@
}
java_library_static {
- name: "launcher-log-protos-lite",
+ name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
"proto_overrides/*.proto",
@@ -45,4 +45,5 @@
"proto_overrides",
],
},
+ static_libs: ["libprotobuf-java-lite"],
}
diff --git a/Android.mk b/Android.mk
index 66ccae0..c066a12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -48,7 +48,9 @@
androidx.preference_preference \
iconloader_base
-LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ LauncherPluginLib \
+ launcher_log_protos_lite
LOCAL_SRC_FILES := \
$(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -213,7 +218,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 26698eb..d747bb8 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!--
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 3c7f308..ec1d55b 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -58,6 +58,35 @@
optional TipType tip_type = 17;
optional int32 search_query_length = 18;
optional bool is_work_app = 19;
+ optional FromFolderLabelState from_folder_label_state = 20;
+ optional ToFolderLabelState to_folder_label_state = 21;
+
+ // Note: proto does not support duplicate enum values, even if they belong to different enum type.
+ // Hence "FROM" and "TO" prefix added.
+ enum FromFolderLabelState{
+ FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ FROM_EMPTY = 1;
+ FROM_CUSTOM = 2;
+ FROM_SUGGESTED = 3;
+ }
+
+ enum ToFolderLabelState{
+ TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
+ TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
+ TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
+ TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
+ TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
+ TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
+ TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
+ UNCHANGED = 14;
+ }
}
// Used to define what type of item a Target would represent.
@@ -141,7 +170,8 @@
AUTOMATED = 1;
COMMAND = 2;
TIP = 3;
- // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+ SOFT_KEYBOARD = 4;
+ // HARD_KEYBOARD, ASSIST
}
enum Touch {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 5d871c3..d3cec28 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index f82af62..834e6cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -296,7 +296,7 @@
predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
predictedApps.add(predictedApp);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 482348f..345a147 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -917,6 +917,12 @@
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
// Finalize the state and notify of the change
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
@@ -938,6 +944,12 @@
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
if (target == NEW_TASK && mRecentsView != null
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
// We are about to launch the current running task, so use LAST_TASK state
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 321af6c..ef1698e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -1251,7 +1251,7 @@
}
public PendingAnimation createAllTasksDismissAnimation(long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
AnimatorSet anim = new AnimatorSet();
@@ -1595,7 +1595,7 @@
}
public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 6c65e01..b3875ae 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -130,7 +130,7 @@
final LinkedList<Runnable> callbacks;
if (mCallbacks.indexOfKey(stateMask) >= 0) {
callbacks = mCallbacks.get(stateMask);
- if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
+ if (FeatureFlags.IS_STUDIO_BUILD && callbacks.contains(callback)) {
throw new IllegalStateException("Existing callback for state found");
}
} else {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index acf61b4..0f98b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -62,6 +62,12 @@
mListeners.remove(listener);
}
+ @UiThread
+ public void removeAllListeners() {
+ Preconditions.assertUIThread();
+ mListeners.clear();
+ }
+
public void notifyAnimationCanceled() {
mCancelled = true;
onAnimationCanceled(null);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d0afb21..4b33d21 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -180,7 +180,7 @@
mDefaultDisplay.addChangeListener(this);
}
- if (mMode == NO_BUTTON) {
+ if (newMode == NO_BUTTON) {
mExclusionListener.register();
} else {
mExclusionListener.unregister();
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index e3e8ace..6902e37 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -55,7 +55,7 @@
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("TaskAnimationManager", msg, new Exception());
@@ -156,9 +156,9 @@
mTargets.release();
}
- // Remove gesture state from callbacks
- if (mCallbacks != null && mLastGestureState != null) {
- mCallbacks.removeListener(mLastGestureState);
+ // Clean up all listeners to ensure we don't get subsequent callbacks
+ if (mCallbacks != null) {
+ mCallbacks.removeAllListeners();
}
mController = null;
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
index 295ab48..d2a0951 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
@@ -16,7 +16,10 @@
package com.android.quickstep.interaction;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.View;
import android.view.Window;
@@ -26,6 +29,7 @@
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
+import java.util.List;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial in full screen mode. */
@@ -47,6 +51,12 @@
}
@Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ disableSystemGestures();
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
@@ -70,4 +80,14 @@
| View.SYSTEM_UI_FLAG_FULLSCREEN);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
+
+ private void disableSystemGestures() {
+ Display display = getDisplay();
+ if (display != null) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ getWindow().setSystemGestureExclusionRects(
+ List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
index 32d0d53..cb04e5b 100644
--- a/quickstep/src/com/android/quickstep/util/BinderTracker.java
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -31,7 +31,7 @@
private static final String TAG = "BinderTracker";
public static void start() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
@@ -40,7 +40,7 @@
}
public static void stop() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 9cf55b3..1d8a79f 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -84,6 +84,7 @@
float taskWidth, taskHeight, paddingHorz;
Resources res = context.getResources();
Rect insets = dp.getInsets();
+ final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
if (dp.isMultiWindowMode) {
if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
@@ -113,7 +114,7 @@
final int paddingResId;
if (dp.isVerticalBarLayout()) {
paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
+ } else if (overviewActionsEnabled && removeShelfFromOverview(context)) {
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
} else {
paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -121,9 +122,11 @@
paddingHorz = res.getDimension(paddingResId);
}
- float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+ float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
float bottomMargin = thumbnailBottomMargin(context);
- float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
+
+ float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
+ ? 0 : res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
// we override the insets ourselves.
@@ -141,7 +144,7 @@
// Center in the visible space
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
float y = insets.top + Math.max(topIconMargin,
- (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+ (launcherVisibleHeight - extraVerticalSpace - outHeight - bottomMargin) / 2);
outRect.set(Math.round(x), Math.round(y),
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 3b3e1c7..724af66 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -47,7 +47,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -259,10 +258,8 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ // b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
@TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
- // b/143285809
- @Ignore
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index c02753f..1c18076 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -23,14 +23,14 @@
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/work_apps_paused_title"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:tint="?attr/folderTextColor"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?attr/workProfileOverlayTextColor"
android:src="@drawable/ic_corp_off" />
<TextView
style="@style/TextHeadline"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -43,7 +43,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
android:textSize="16sp" />
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index db95416..2cffedd 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -32,6 +32,8 @@
android:layout_weight="1"
android:drawableStart="@drawable/ic_corp"
android:drawablePadding="3dp"
+ android:drawableTint="?attr/workProfileOverlayTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index aef878b..5a15ec6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -41,6 +41,7 @@
<attr name="folderIconBorderColor" format="color" />
<attr name="folderTextColor" format="color" />
<attr name="folderHintColor" format="color" />
+ <attr name="workProfileOverlayTextColor" format="color" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5faa429..9d0fb56 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -142,7 +142,7 @@
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
<!-- Default folder title -->
- <string name="folder_hint_text">Tap to edit</string>
+ <string name="folder_hint_text">Edit Name</string>
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1174a2f..cee268b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,6 +50,7 @@
<item name="folderTextColor">#FF212121</item>
<item name="folderHintColor">#FF616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
+ <item name="workProfileOverlayTextColor">#FF212121</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -77,6 +78,7 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
+
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -100,6 +102,7 @@
<item name="folderHintColor">#FFCCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
+ <item name="workProfileOverlayTextColor">@android:color/white</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ac61c49..8718820 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -538,7 +538,7 @@
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 466b7b2..f07040d 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -96,7 +96,7 @@
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 94aadb2..3fc8de2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2143,7 +2143,7 @@
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4cd038d..d77285d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -91,7 +91,7 @@
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index bb6c330..f618fe1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
-import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -55,7 +55,6 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
@@ -67,7 +66,6 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -128,16 +126,6 @@
}
/**
- * Returns AppInfo with corresponding package name.
- * TODO: move to enqueueModelTask
- */
- public Optional<AppInfo> getAppInfoByPackageName(String pkg) {
- return mBgAllAppsList.data.stream()
- .filter(info -> info.componentName.getPackageName().equals(pkg))
- .findAny();
- }
-
- /**
* Adds the provided items to the workspace.
*/
public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
@@ -250,7 +238,7 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
- } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+ } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
for (Callbacks cb : getCallbacks()) {
if (cb instanceof Launcher) {
((Launcher) cb).recreate();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5544240..697048a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -119,7 +119,7 @@
@Override
public boolean onCreate() {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Launcher process started");
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ae4eae9..4c6e1f3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1502,7 +1502,7 @@
return false;
}
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
duration *= Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 1);
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b7f8547..a8f492f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1858,7 +1858,7 @@
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2153,7 +2153,7 @@
ItemInfo item = d.dragInfo;
if (item == null) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -2775,7 +2775,7 @@
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
}
@@ -2794,7 +2794,7 @@
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index e66cf39..fcccc59 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -155,6 +155,11 @@
return mMultiValueAlpha.getProperty(index);
}
+ public WorkFooterContainer getWorkFooterContainer() {
+ return mWorkFooterContainer;
+ }
+
+
@Override
protected void setDampedScrollShift(float shift) {
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ea549b9..6413044 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -43,7 +43,10 @@
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
}
- public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
+ /**
+ * True when the build has come from Android Studio and is being used for local debugging.
+ */
+ public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG;
/**
* Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 1b7b015..de7bc6d 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -160,8 +160,8 @@
mOptions = options;
if (mOptions.systemDndStartPoint != null) {
- mMotionDownX = mOptions.systemDndStartPoint.x;
- mMotionDownY = mOptions.systemDndStartPoint.y;
+ mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
+ mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
}
final int registrationX = mMotionDownX - dragLayerX;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 544efd5..1d79e01 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -16,9 +16,23 @@
package com.android.launcher3.folder;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,22 +89,24 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Action;
+import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -188,6 +204,9 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
+ private String mPreviousLabel;
+ private boolean mIsPreviousLabelSuggested;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -302,12 +321,12 @@
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- if (TextUtils.isEmpty(mFolderName.getText())) {
+ if (isEmpty(mFolderName.getText())) {
FolderNameInfo[] nameInfos =
(FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
if (nameInfos != null) {
- showLabelSuggestion(nameInfos);
+ showLabelSuggestion(nameInfos, false);
}
}
}
@@ -326,23 +345,16 @@
}
mInfo.title = newTitle;
- mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mFolderName.setText(mInfo.title);
- // TODO: depending on whether the title was manually edited or automatically
- // suggested, apply different hint.
- mFolderName.setHint("");
+ if (TextUtils.isEmpty(mInfo.title)) {
+ mFolderName.setHint(R.string.folder_hint_text);
+ mFolderName.setText("");
} else {
- if (TextUtils.isEmpty(mInfo.title)) {
- mFolderName.setHint(R.string.folder_hint_text);
- mFolderName.setText("");
- } else {
- mFolderName.setHint(null);
- }
+ mFolderName.setHint(null);
}
sendCustomAccessibilityEvent(
@@ -425,8 +437,10 @@
mItemsInvalidated = true;
mInfo.addListener(this);
- if (!TextUtils.isEmpty(mInfo.title)) {
+ if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
+ mPreviousLabel = mInfo.title.toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
@@ -452,9 +466,9 @@
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
nameInfos);
- if (TextUtils.isEmpty(mFolderName.getText().toString())
- && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
- showLabelSuggestion(nameInfos);
+ if (isEmpty(mFolderName.getText().toString())
+ && !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
+ showLabelSuggestion(nameInfos, true);
}
}
}
@@ -463,28 +477,29 @@
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
* InputMethodManager suggestions.
*/
- private void showLabelSuggestion(FolderNameInfo[] nameInfos) {
+ private void showLabelSuggestion(FolderNameInfo[] nameInfos, boolean animate) {
if (nameInfos == null) {
return;
}
// Open the Folder and Keyboard when the first or second suggestion is valid non-empty
// string.
- boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !TextUtils.isEmpty(
+ boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
nameInfos[0].getLabel())
- || nameInfos.length > 1 && nameInfos[1] != null && !TextUtils.isEmpty(
+ || nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
nameInfos[1].getLabel());
CharSequence firstLabel = nameInfos[0].getLabel();
if (shouldOpen) {
- if (!TextUtils.isEmpty(firstLabel)) {
+ if (!isEmpty(firstLabel)) {
mFolderName.setHint("");
mFolderName.setText(firstLabel);
- mInfo.title = firstLabel;
}
- animateOpen(mInfo.contents, 0, true);
+ if (animate) {
+ animateOpen(mInfo.contents, 0, true);
+ }
mFolderName.showKeyboard();
mFolderName.displayCompletions(
- Arrays.asList(nameInfos).subList(1, nameInfos.length).stream()
+ asList(nameInfos).subList(1, nameInfos.length).stream()
.filter(Objects::nonNull)
.map(s -> s.getLabel().toString())
.collect(Collectors.toList()));
@@ -608,7 +623,7 @@
dragLayer.addView(this);
mDragController.addDropTarget(this);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ getParent());
}
@@ -636,9 +651,9 @@
if (!skipUserEventLog) {
mLauncher.getUserEventDispatcher().logActionOnItem(
- Touch.TAP,
- Direction.NONE,
- ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+ LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.Action.Direction.NONE,
+ LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
}
@@ -1420,6 +1435,7 @@
if (hasFocus) {
startEditingFolderName();
} else {
+ logEditFolderLabel();
mFolderName.dispatchBackKey();
}
}
@@ -1433,11 +1449,12 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
target.gridX = info.cellX;
target.gridY = info.cellY;
target.pageIndex = mContent.getCurrentPage();
- targetParent.containerType = ContainerType.FOLDER;
+ targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
}
private class OnScrollHintListener implements OnAlarmListener {
@@ -1535,7 +1552,7 @@
@Override
public int getLogContainerType() {
- return ContainerType.FOLDER;
+ return LauncherLogProto.ContainerType.FOLDER;
}
/**
@@ -1570,7 +1587,7 @@
}
} else {
mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(ContainerType.FOLDER));
+ LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
close(true);
return true;
}
@@ -1600,4 +1617,113 @@
super.draw(canvas);
}
}
+
+ private void logEditFolderLabel() {
+ LauncherEvent launcherEvent = LauncherEvent.newBuilder()
+ .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
+ .addSrcTarget(newEditTextTargetBuilder()
+ .setFromFolderLabelState(getFromFolderLabelState())
+ .setToFolderLabelState(getToFolderLabelState()))
+ .addSrcTarget(newFolderTargetBuilder())
+ .addSrcTarget(newParentContainerTarget())
+ .build();
+ mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
+ mPreviousLabel = mFolderName.getText().toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+ }
+
+ private Target.FromFolderLabelState getFromFolderLabelState() {
+ return mPreviousLabel == null
+ ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
+ : mPreviousLabel.isEmpty()
+ ? FROM_EMPTY
+ : mIsPreviousLabelSuggested
+ ? FROM_SUGGESTED
+ : FROM_CUSTOM;
+ }
+
+ private Target.ToFolderLabelState getToFolderLabelState() {
+ String newLabel =
+ checkNotNull(mFolderName.getText().toString(),
+ "Expected valid folder label, but found null");
+
+ Optional<String[]> suggestedLabels = Optional.ofNullable(
+ (FolderNameInfo[]) mInfo.suggestedFolderNames
+ .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+
+
+ int accepted_suggestion_index = suggestedLabels
+ .map(folderNameInfoArray ->
+ IntStream.range(0, folderNameInfoArray.length)
+ .filter(index -> newLabel.equalsIgnoreCase(
+ folderNameInfoArray[index]))
+ .findFirst()
+ .orElse(-1)
+ ).orElse(-1);
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
+ .orElse(false);
+ String primarySuffix = hasValidPrimary
+ ? "_WITH_VALID_PRIMARY"
+ : "_WITH_EMPTY_PRIMARY";
+
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
+ String suggestionsSuffix = isSuggestionsEnabled
+ ? isEmptySuggestions
+ ? "_WITH_EMPTY_SUGGESTIONS"
+ : "_WITH_VALID_SUGGESTIONS"
+ : "_WITH_SUGGESTIONS_DISABLED";
+
+ return newLabel.equals(mPreviousLabel)
+ ? Target.ToFolderLabelState.UNCHANGED
+ : newLabel.isEmpty()
+ ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
+ : accepted_suggestion_index >= 0
+ ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
+ + accepted_suggestion_index
+ + primarySuffix)
+ : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
+ }
+
+
+ private Target.Builder newEditTextTargetBuilder() {
+ return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
+ }
+
+ private Target.Builder newFolderTargetBuilder() {
+ return Target.newBuilder()
+ .setType(Target.Type.CONTAINER)
+ .setContainerType(ContainerType.FOLDER)
+ .setPageIndex(mInfo.screenId)
+ .setGridX(mInfo.cellX)
+ .setGridY(mInfo.cellY)
+ .setCardinality(mInfo.contents.size());
+ }
+
+ private Target.Builder newParentContainerTarget() {
+ Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+
+ switch (mInfo.container) {
+ case CONTAINER_HOTSEAT:
+ return builder.setContainerType(ContainerType.HOTSEAT);
+ case CONTAINER_DESKTOP:
+ return builder.setContainerType(ContainerType.WORKSPACE);
+ default:
+ throw new AssertionError(String
+ .format("Expected container to be either %s or %s but found %s.",
+ CONTAINER_HOTSEAT,
+ CONTAINER_DESKTOP,
+ mInfo.container));
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index d5990fa..184dbb9 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -21,9 +21,14 @@
import android.util.Log;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
@@ -50,6 +55,8 @@
* name edit box can also be used to provide suggestion.
*/
public static final int SUGGEST_MAX = 4;
+ protected IntSparseArrayMap<FolderInfo> mFolderInfos;
+ protected List<AppInfo> mAppInfos;
/**
* Retrieve instance of this object that can be overridden in runtime based on the build
@@ -58,9 +65,30 @@
public static FolderNameProvider newInstance(Context context) {
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(context);
+
return fnp;
}
+ public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
+ IntSparseArrayMap<FolderInfo> folderInfos) {
+ FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
+ context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(appInfos, folderInfos);
+
+ return fnp;
+ }
+
+ private void load(Context context) {
+ LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
+ new FolderNameWorker());
+ }
+
+ private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
+ mAppInfos = appInfos;
+ mFolderInfos = folderInfos;
+ }
+
/**
* Generate and rank the suggested Folder names.
*/
@@ -92,9 +120,8 @@
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1) {
- Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
- .getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
- .getPackageName());
+ Optional<AppInfo> info = getAppInfoByPackageName(
+ distinctItemInfos.get(0).getTargetComponent().getPackageName());
// Place it as first viable suggestion and shift everything else
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
@@ -103,6 +130,15 @@
}
}
+ private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
+ if (mAppInfos == null || mAppInfos.isEmpty()) {
+ return Optional.empty();
+ }
+ return mAppInfos.stream()
+ .filter(info -> info.componentName.getPackageName().equals(packageName))
+ .findAny();
+ }
+
private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
@@ -143,4 +179,13 @@
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
+
+ private class FolderNameWorker extends BaseModelUpdateTask {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ mFolderInfos = dataModel.folders.clone();
+ mAppInfos = Arrays.asList(apps.copyData());
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f579451..94d30f6 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -152,7 +152,7 @@
}
public final void generateDragOutline(Bitmap preview) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
throw new RuntimeException("Drag outline generated twice");
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 8289da9..199d13f 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -69,8 +69,7 @@
public class UserEventDispatcher implements ResourceBasedOverride {
private static final String TAG = "UserEvent";
- private static final boolean IS_VERBOSE =
- FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+ private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@
dispatchUserEvent(event, null);
}
+ /**
+ * Logs proto lite version of LauncherEvent object to clearcut.
+ */
+ public void logLauncherEvent(
+ com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+
+ if (mPreviousHomeGesture) {
+ mPreviousHomeGesture = false;
+ }
+ mAppOrTaskLaunch = false;
+ launcherEvent.toBuilder()
+ .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+ .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+ if (!IS_VERBOSE) {
+ return;
+ }
+ Log.d(TAG, launcherEvent.toString());
+ }
+
public void logDeepShortcutsOpen(View icon) {
LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index c24b939..32fce0b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -273,7 +273,7 @@
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index af802ef..6223a23 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -901,7 +901,8 @@
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext());
+ FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ mBgAllAppsList.data, mBgDataModel.folders);
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 628dd95..1473124 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -97,7 +97,7 @@
return Integer.compare(lhs.screenId, rhs.screenId);
}
default:
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException(
"Unexpected container type when sorting workspace items.");
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ccd1554..27fa580 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -115,8 +115,9 @@
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
- if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
- modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
+ if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_STUDIO_BUILD
+ && modelItem instanceof WorkspaceItemInfo
+ && item instanceof WorkspaceItemInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
@@ -320,7 +321,7 @@
*/
public void prepareToUndoDelete() {
if (!mPreparingToUndo) {
- if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("There are still uncommitted delete operations!");
}
mDeleteRunnables.clear();
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 4a4a5ca..fcb96d7 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
@@ -67,7 +66,7 @@
* Utility method to debug binary data
*/
public static String createTempFile(Context context, byte[] data) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Method only allowed in development mode");
}
@@ -87,7 +86,7 @@
try {
c.close();
} catch (IOException e) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Error closing", e);
}
}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index ed66422..63d56e4 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -28,25 +28,25 @@
public class Preconditions {
public static void assertNotNull(Object o) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && o == null) {
throw new IllegalStateException();
}
}
public static void assertWorkerThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
throw new IllegalStateException();
}
}
public static void assertUIThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
public static void assertNonUiThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index f8add9a..9ac8230 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -119,6 +119,13 @@
mWorkModeSwitch.refresh();
}
+ /**
+ * Returns work mode switch
+ */
+ public WorkModeSwitch getWorkModeSwitch() {
+ return mWorkModeSwitch;
+ }
+
private boolean shouldShowWorkFooter() {
Launcher launcher = Launcher.getLauncher(getContext());
return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 5ebf8d3..282867a 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -115,7 +115,7 @@
}
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+ if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/tests/Android.mk b/tests/Android.mk
index d1a6c06..a9fff8e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -51,7 +51,7 @@
androidx.test.rules \
androidx.test.uiautomator_uiautomator \
mockito-target-minus-junit4 \
- launcher-log-protos-lite
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index d9edc35..5aa0090 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -16,17 +16,32 @@
package com.android.launcher3.ui;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.views.WorkFooterContainer;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+import java.util.Objects;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
public class WorkTabTest extends AbstractLauncherUiTest {
@@ -52,16 +67,50 @@
}
@Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void workTabExists() {
mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- /*
- assertTrue("Personal tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isPersonalTabVisible()));
- assertTrue("Work tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isWorkTabVisible()));
- */
+ waitForLauncherCondition("Personal tab is missing",
+ launcher -> launcher.getAppsView().isPersonalTabVisible());
+ waitForLauncherCondition("Work tab is missing",
+ launcher -> launcher.getAppsView().isWorkTabVisible());
}
+
+ @Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+ public void toggleWorks() {
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+ getOnceNotNull("Apps view did not bind",
+ launcher -> launcher.getAppsView().getWorkFooterContainer());
+
+ UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
+ assertEquals(2, userManager.getUserProfiles().size());
+ UserHandle workProfile = getFromLauncher(l -> {
+ UserHandle myHandle = Process.myUserHandle();
+ List<UserHandle> userProfiles = userManager.getUserProfiles();
+ return userProfiles.get(0) == myHandle ? userProfiles.get(1) : userProfiles.get(0);
+ });
+
+ waitForLauncherCondition("work profile can't be turned off",
+ l -> userManager.requestQuietModeEnabled(true, workProfile));
+
+ assertTrue(userManager.isQuietModeEnabled(workProfile));
+ executeOnLauncher(launcher -> {
+ WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
+ ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
+ AllAppsContainerView.AdapterHolder.WORK);
+ wf.getWorkModeSwitch().toggle();
+ });
+ waitForLauncherCondition("Work toggle did not work",
+ l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
+ }
+
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index abd0f24..c5fbd7c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1251,13 +1251,14 @@
private Map<String, List<String>> getEvents() {
final Map<String, List<String>> events = new HashMap<>();
try {
- // Logcat may skip events after the specified time. Querying for events starting 1 sec
+ // Logcat may skip events after the specified time. Querying for events starting 1 min
// earlier.
- final Date startTime = new Date(mStartRecordingTime.getTime() - 10000);
- final String logcatEvents = mDevice.executeShellCommand(
- "logcat -d -v year --pid=" + getPid() + " -t "
- + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
- + " -s " + TestProtocol.TAPL_EVENTS_TAG);
+ final Date startTime = new Date(mStartRecordingTime.getTime() - 60000);
+ final String logcatCommand = "logcat -d -v year --pid=" + getPid() + " -t "
+ + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
+ + " -s " + TestProtocol.TAPL_EVENTS_TAG;
+ log("Events query command: " + logcatCommand);
+ final String logcatEvents = mDevice.executeShellCommand(logcatCommand);
final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
while (matcher.find()) {
// Skip events before recording start time.