Merge "Fixing sysui visibility changing multiple times on startup" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index c6e8c20b..d1185bd 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -53,6 +53,8 @@
SearchResultContainer search_result_container = 7;
ShortcutsContainer shortcuts_container = 8;
SettingsContainer settings_container = 9;
+ PredictedHotseatContainer predicted_hotseat_container = 10;
+ TaskSwitcherContainer task_switcher_container = 11;
}
}
@@ -81,6 +83,9 @@
message SettingsContainer {
}
+message TaskSwitcherContainer {
+}
+
enum Attribute {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
@@ -151,6 +156,14 @@
optional int32 index = 1;
}
+// Represents hotseat container with prediction feature enabled.
+message PredictedHotseatContainer {
+ optional int32 index = 1;
+
+ // No of hotseat positions filled with predicted items.
+ optional int32 cardinality = 2;
+}
+
message FolderContainer {
optional int32 page_index = 1 [default = -1];
optional int32 grid_x = 2 [default = -1];
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index b6a8206..f881610 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -23,6 +23,7 @@
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Process;
import androidx.annotation.NonNull;
@@ -35,6 +36,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
+import com.android.launcher3.hybridhotseat.HotseatFileLog;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.data.ItemInfo;
@@ -310,6 +312,18 @@
*/
public static void fillInPredictedRank(
@NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
+
+ HotseatFileLog hotseatFileLog = HotseatFileLog.INSTANCE.getNoCreate();
+
+ if (hotseatFileLog != null && itemInfo != null && Utilities.IS_DEBUG_DEVICE) {
+ final String pkg = itemInfo.getTargetComponent() != null
+ ? itemInfo.getTargetComponent().getPackageName() : "unknown";
+ hotseatFileLog.log("UserEvent",
+ "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
+ && !Process.myUserHandle().equals(itemInfo.user))
+ + ",launchLocation:" + itemInfo.container);
+ }
+
final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate();
if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null
|| (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index f1ce72e..5d807d3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.hybridhotseat;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
+ .LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
+
import android.content.Intent;
import android.view.View;
@@ -47,12 +50,12 @@
public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen";
public static final String HOTSEAT_EDU_ACTION =
"com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
- private static final String SETTINGS_ACTION =
+ public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
private final Launcher mLauncher;
private final Hotseat mHotseat;
- private final HotseatRestoreHelper mRestoreHelper;
+ private HotseatRestoreHelper mRestoreHelper;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
@@ -71,14 +74,17 @@
* Checks what type of migration should be used and migrates hotseat
*/
void migrate() {
- mRestoreHelper.createBackup();
+ if (mRestoreHelper != null) {
+ mRestoreHelper.createBackup();
+ }
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
migrateToFolder();
} else {
migrateHotseatWhole();
}
- Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off,
- null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
}
/**
@@ -223,15 +229,15 @@
void finishOnboarding() {
mOnOnboardingComplete.run();
- destroy();
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
}
void showDimissTip() {
if (mHotseat.getShortcutsAndWidgets().getChildCount()
< mLauncher.getDeviceProfile().inv.numHotseatIcons) {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, R.string.hotseat_turn_off,
- null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
} else {
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
@@ -242,12 +248,6 @@
mPredictedApps = predictedApps;
}
- void destroy() {
- if (mActiveDialog != null) {
- mActiveDialog.setHotseatEduController(null);
- }
- }
-
void showEdu() {
int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID);
@@ -265,6 +265,7 @@
requiresMigration ? R.string.hotseat_tip_no_empty_slots
: R.string.hotseat_auto_enrolled),
mHotseat.getTop());
+ mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
finishOnboarding();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 99cb3b3..96be5df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -15,9 +15,10 @@
*/
package com.android.launcher3.hybridhotseat;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
- .HYBRID_HOTSEAT_CANCELED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
+ .LAUNCHER_HOTSEAT_EDU_ACCEPT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -29,15 +30,14 @@
import android.widget.Button;
import android.widget.TextView;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -112,15 +112,13 @@
mHotseatEduController.moveHotseatItems();
mHotseatEduController.finishOnboarding();
- //TODO: pass actual page index here.
- // Temporarily we're passing 1 for folder migration and 2 for page migration
- logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
+ mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
}
private void onDismiss(View v) {
mHotseatEduController.showDimissTip();
mHotseatEduController.finishOnboarding();
- logUserAction(false, -1);
+ mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_DENY);
handleClose(true);
}
@@ -164,39 +162,6 @@
}
}
- private void logUserAction(boolean migrated, int pageIndex) {
- LauncherLogProto.Action action = new LauncherLogProto.Action();
- LauncherLogProto.Target target = new LauncherLogProto.Target();
-
- int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount();
- // -1 to exclude smart space
- int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
- Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
-
- action.type = LauncherLogProto.Action.Type.TOUCH;
- action.touch = LauncherLogProto.Action.Touch.TAP;
- target.containerType = LauncherLogProto.ContainerType.TIP;
- target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
- target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
- : HYBRID_HOTSEAT_CANCELED;
- target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
- // encoding migration type on pageIndex
- target.pageIndex = pageIndex;
- target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
- LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
- UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
- }
-
- private void logOnBoardingSeen() {
- LauncherLogProto.Action action = new LauncherLogProto.Action();
- LauncherLogProto.Target target = new LauncherLogProto.Target();
- action.type = LauncherLogProto.Action.Type.TIP;
- target.containerType = LauncherLogProto.ContainerType.TIP;
- target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
- LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
- UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
- }
-
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -245,8 +210,9 @@
|| mHotseatEduController == null) {
return;
}
+ AbstractFloatingView.closeAllOpenViews(mLauncher);
attachToContainer();
- logOnBoardingSeen();
+ mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_SEEN);
animateOpen();
populatePreview(predictions);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
new file mode 100644
index 0000000..c15a596
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.hybridhotseat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Helper class to allow hot seat file logging
+ */
+public class HotseatFileLog {
+
+ public static final int LOG_DAYS = 10;
+ private static final String FILE_NAME_PREFIX = "hotseat-log-";
+ private static final DateFormat DATE_FORMAT =
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+ public static final MainThreadInitializedObject<HotseatFileLog> INSTANCE =
+ new MainThreadInitializedObject<>(HotseatFileLog::new);
+
+
+ private final Handler mHandler = new Handler(
+ Executors.createAndStartNewLooper("hotseat-logger"));
+ private final File mLogsDir;
+ private PrintWriter mCurrentWriter;
+ private String mFileName;
+
+ private HotseatFileLog(Context context) {
+ mLogsDir = context.getFilesDir();
+ }
+
+ /**
+ * Prints log values to disk
+ */
+ public void log(String tag, String msg) {
+ String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
+
+ mHandler.post(() -> {
+ synchronized (this) {
+ PrintWriter writer = getWriter();
+ if (writer != null) {
+ writer.println(out);
+ }
+ }
+ });
+ }
+
+ private PrintWriter getWriter() {
+ String fName = FILE_NAME_PREFIX + (LOG_DAYS % 10);
+ if (fName.equals(mFileName)) return mCurrentWriter;
+
+ Calendar cal = Calendar.getInstance();
+
+ boolean append = false;
+ File logFile = new File(mLogsDir, fName);
+ if (logFile.exists()) {
+ Calendar modifiedTime = Calendar.getInstance();
+ modifiedTime.setTimeInMillis(logFile.lastModified());
+
+ // If the file was modified more that 36 hours ago, purge the file.
+ // We use instead of 24 to account for day-365 followed by day-1
+ modifiedTime.add(Calendar.HOUR, 36);
+ append = cal.before(modifiedTime);
+ }
+
+
+ if (mCurrentWriter != null) {
+ mCurrentWriter.close();
+ }
+ try {
+ mCurrentWriter = new PrintWriter(new FileWriter(logFile, append));
+ mFileName = fName;
+ } catch (Exception ex) {
+ Log.e("HotseatLogs", "Error writing logs to file", ex);
+ closeWriter();
+ }
+ return mCurrentWriter;
+ }
+
+
+ private synchronized void closeWriter() {
+ mFileName = null;
+ if (mCurrentWriter != null) {
+ mCurrentWriter.close();
+ }
+ mCurrentWriter = null;
+ }
+
+
+ /**
+ * Returns a list of all log files
+ */
+ public synchronized File[] getLogFiles() {
+ File[] files = new File[LOG_DAYS + FileLog.LOG_DAYS];
+ //include file log files here
+ System.arraycopy(FileLog.getLogFiles(), 0, files, 0, FileLog.LOG_DAYS);
+
+ closeWriter();
+ for (int i = 0; i < LOG_DAYS; i++) {
+ files[FileLog.LOG_DAYS + i] = new File(mLogsDir, FILE_NAME_PREFIX + i);
+ }
+ return files;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index bd4d713..6ca07bb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -27,6 +28,7 @@
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.content.ComponentName;
+import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -50,7 +52,6 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -64,6 +65,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -107,8 +110,6 @@
private boolean mIsCacheEmpty;
private boolean mIsDestroyed = false;
- private HotseatEduController mHotseatEduController;
-
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
@@ -146,11 +147,48 @@
}
/**
- * Transitions to NORMAL workspace mode and shows edu
+ * Shows appropriate hotseat education based on prediction enabled and migration states.
*/
public void showEdu() {
- if (mHotseatEduController == null) return;
- mHotseatEduController.showEdu();
+ if (mComponentKeyMappers.isEmpty()) {
+ // launcher has empty predictions set
+ Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(
+ new Intent(SETTINGS_ACTION)));
+ } else if (isEduSeen()) {
+ // user has already went through education
+ new ArrowTipView(mLauncher).show(
+ mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
+ mHotseat.getTop());
+ } else {
+ HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
+ this::createPredictor);
+ eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
+ eduController.showEdu();
+ }
+ }
+
+ /**
+ * Shows educational tip for hotseat if user does not go through Tips app.
+ */
+ public void showDiscoveryTip() {
+ if (getPredictedIcons().size() == mHotSeatItemsCount) {
+ new ArrowTipView(mLauncher).show(
+ mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+ } else {
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ }
+ }
+
+ /**
+ * Returns if hotseat client has predictions
+ * @return
+ */
+ public boolean hasPredictions() {
+ return !mComponentKeyMappers.isEmpty();
}
@Override
@@ -250,10 +288,6 @@
if (mAppPredictor != null) {
mAppPredictor.destroy();
}
- if (mHotseatEduController != null) {
- mHotseatEduController.destroy();
- mHotseatEduController = null;
- }
}
/**
@@ -299,20 +333,20 @@
mAppPredictor.requestPredictionUpdate();
});
setPauseUIUpdate(false);
- if (!isEduSeen()) {
- mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
- this::createPredictor);
- }
}
/**
* Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
*/
- public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+ public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+ if (hasPredictions() && mAppPredictor != null) {
+ mAppPredictor.requestPredictionUpdate();
+ fillGapsWithPrediction();
+ return;
+ }
mIsCacheEmpty = apps.isEmpty();
int count = Math.min(ranks.size(), apps.size());
List<WorkspaceItemInfo> items = new ArrayList<>(count);
- mComponentKeyMappers.clear();
for (int i = 0; i < count; i++) {
WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
@@ -347,12 +381,11 @@
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
predictionLog.append("]");
- if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
+ if (Utilities.IS_DEBUG_DEVICE) {
+ HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString());
+ }
updateDependencies();
fillGapsWithPrediction();
- if (!isEduSeen() && mHotseatEduController != null) {
- mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
- }
cachePredictionComponentKeysIfNecessary(componentKeys);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index ce6bb7d..0ace4cc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -57,7 +57,7 @@
LauncherAccessibilityDelegate.AccessibilityActionHandler {
private static final int RING_SHADOW_COLOR = 0x99000000;
- private static final float RING_EFFECT_RATIO = 0.11f;
+ private static final float RING_EFFECT_RATIO = 0.08f;
boolean mIsDrawingDot = false;
private final DeviceProfile mDeviceProfile;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 494a98d..3b45ec9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -88,7 +88,6 @@
*/
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
- private HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -169,13 +168,6 @@
}
/**
- * Returns Prediction controller for hybrid hotseat
- */
- public HotseatPredictionController getHotseatPredictionController() {
- return mHotseatPredictionController;
- }
-
- /**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*/
private void onStateOrResumeChanging(boolean inTransition) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 79dc3e2..ba8656d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -156,6 +156,7 @@
if (toState == NORMAL && fromState == OVERVIEW) {
config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
@@ -210,6 +211,7 @@
}
}
config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index b5fb31a..f5c5874 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -32,7 +32,6 @@
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.VibratorWrapper;
@@ -96,8 +95,8 @@
* depend on proper class initialization.
*/
protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
- .getDeviceProfile(mContext));
+ initTransitionEndpoints(
+ mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
}
protected void performHapticFeedback() {
@@ -144,13 +143,14 @@
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
mGestureState.updateLastStartedTaskId(taskId);
+ boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+ .contains(taskId);
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
if (success) {
- if (mRecentsView.indexOfChild(nextTask)
- == getLastAppearedTaskIndex()) {
- onRestartLastAppearedTask();
+ if (hasTaskPreviouslyAppeared) {
+ onRestartPreviouslyAppearedTask();
}
} else {
mActivityInterface.onLaunchTaskFailed();
@@ -171,7 +171,7 @@
* start A again to ensure it stays on top.
*/
@CallSuper
- protected void onRestartLastAppearedTask() {
+ protected void onRestartPreviouslyAppearedTask() {
// Finish the controller here, since we won't get onTaskAppeared() for a task that already
// appeared.
if (mRecentsAnimationController != null) {
@@ -205,7 +205,7 @@
mRecentsAnimationController = recentsAnimationController;
mRecentsAnimationTargets = targets;
mTransformParams.setTargetSet(mRecentsAnimationTargets);
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
+ DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
mGestureState.getRunningTaskId());
@@ -300,8 +300,7 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
}
- initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
- .getDeviceProfile(mContext));
+ initTransitionEndpoints(createdActivity.getDeviceProfile());
}
return true;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index f2438b6..d55dc0f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -41,6 +41,7 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
@@ -78,9 +79,11 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
/**
* Handles the navigation gestures when Launcher is the default home activity.
@@ -900,6 +903,21 @@
protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration);
+ private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ if (task.taskId == mGestureState.getRunningTaskId()) {
+ // Since this is an edge case, just cancel and relaunch with default activity
+ // options (since we don't know if there's an associated app icon to launch from)
+ endRunningWindowAnim(true /* cancel */);
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ mActivityRestartListener);
+ ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null);
+ }
+ }
+ };
+
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
@@ -907,6 +925,13 @@
mGestureState.setEndTarget(target, false /* isAtomic */);
maybeUpdateRecentsAttachedState();
+ // If we are transitioning to launcher, then listen for the activity to be restarted while
+ // the transition is in progress
+ if (mGestureState.getEndTarget().isLauncher) {
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(
+ mActivityRestartListener);
+ }
+
if (mGestureState.getEndTarget() == HOME) {
HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
@@ -1097,8 +1122,8 @@
}
@Override
- protected void onRestartLastAppearedTask() {
- super.onRestartLastAppearedTask();
+ protected void onRestartPreviouslyAppearedTask() {
+ super.onRestartPreviouslyAppearedTask();
reset();
}
@@ -1127,6 +1152,7 @@
}
mActivityInitListener.unregister();
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
mTaskSnapshot = null;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index a28dabc..ebc83c6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -37,7 +37,7 @@
case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile,
- PagedOrientationHandler.HOME_ROTATED);
+ PagedOrientationHandler.PORTRAIT);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
return response;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 852a51a..1701020 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -139,13 +139,12 @@
*/
protected DeviceProfile createDeviceProfile() {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
- DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
// In case we are reusing IDP, create a copy so that we don't conflict with Launcher
// activity.
return (mRecentsRootView != null) && isInMultiWindowMode()
? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
- : dp1.copy(this);
+ : dp.copy(this);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index b17730b..dc8f1c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -83,15 +83,15 @@
mGestureState = gestureState;
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
mTransformParams = transformParams;
+
+ mTaskViewSimulator.setLayoutRotation(
+ mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation());
}
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
mTaskViewSimulator.setDp(dp);
- mTaskViewSimulator.setLayoutRotation(
- mDeviceState.getCurrentActiveRotation(),
- mDeviceState.getDisplayRotation());
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT,
mTaskViewSimulator.getOrientationState().getOrientationHandler());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 97cd11b..a6a08cb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static android.view.Surface.ROTATION_0;
+
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.annotation.SuppressLint;
@@ -24,6 +26,7 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
+import android.view.View;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
@@ -31,9 +34,12 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
@@ -58,6 +64,19 @@
shortcuts.add(shortcut);
}
}
+ RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
+ boolean canLauncherRotate = orientedState.canLauncherRotate();
+ boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
+
+ // Add overview actions to the menu when in in-place rotate landscape mode.
+ if (!canLauncherRotate && isInLandscape) {
+ for (TaskShortcutFactory actionMenuOption : ACTION_MENU_OPTIONS) {
+ SystemShortcut shortcut = actionMenuOption.getShortcut(activity, taskView);
+ if (shortcut != null) {
+ shortcuts.add(shortcut);
+ }
+ }
+ }
return shortcuts;
}
@@ -85,6 +104,11 @@
TaskShortcutFactory.WELLBEING
};
+ private static final TaskShortcutFactory[] ACTION_MENU_OPTIONS = new TaskShortcutFactory[]{
+ TaskShortcutFactory.SCREENSHOT,
+ TaskShortcutFactory.MODAL
+ };
+
/**
* Overlay on each task handling Overview Action Buttons.
*/
@@ -94,10 +118,14 @@
protected final TaskThumbnailView mThumbnailView;
private T mActionsView;
+ private ImageActionsApi mImageApi;
+ private boolean mIsAllowedByPolicy;
protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
mThumbnailView = taskThumbnailView;
+ mImageApi = new ImageActionsApi(
+ mApplicationContext, mThumbnailView::getThumbnail);
}
protected T getActionsView() {
@@ -112,15 +140,12 @@
* Called when the current task is interactive for the user
*/
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
- ImageActionsApi imageApi = new ImageActionsApi(
- mApplicationContext, mThumbnailView::getThumbnail);
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
-
getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override
public void onShare() {
if (isAllowedByPolicy) {
- imageApi.startShareActivity();
+ mImageApi.startShareActivity();
} else {
showBlockedByPolicyMessage();
}
@@ -129,16 +154,23 @@
@SuppressLint("NewApi")
@Override
public void onScreenshot() {
- if (isAllowedByPolicy) {
- imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
- getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
- } else {
- showBlockedByPolicyMessage();
- }
+ saveScreenshot(task);
}
});
}
+ /**
+ * Called to save screenshot of the task thumbnail.
+ */
+ @SuppressLint("NewApi")
+ private void saveScreenshot(Task task) {
+ if (mThumbnailView.isRealSnapshot()) {
+ mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+ getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
+ } else {
+ showBlockedByPolicyMessage();
+ }
+ }
/**
* Called when the overlay is no longer used.
@@ -147,6 +179,20 @@
}
/**
+ * Gets the modal state system shortcut.
+ */
+ public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
+ return null;
+ }
+
+ /**
+ * Gets the system shortcut for the screenshot that will be added to the task menu.
+ */
+ public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
+ ItemInfo iteminfo) {
+ return new ScreenshotSystemShortcut(activity, iteminfo);
+ }
+ /**
* Gets the task snapshot as it is displayed on the screen.
*
* @return the bounds of the snapshot in screen coordinates.
@@ -175,6 +221,22 @@
R.string.blocked_by_policy,
Toast.LENGTH_LONG).show();
}
+
+ private class ScreenshotSystemShortcut extends SystemShortcut {
+
+ private final BaseDraggingActivity mActivity;
+
+ ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo) {
+ super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo);
+ mActivity = activity;
+ }
+
+ @Override
+ public void onClick(View view) {
+ saveScreenshot(mThumbnailView.getTaskView().getTask());
+ dismissTaskMenuView(mActivity);
+ }
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index 3623e67..4eae437 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -18,29 +18,26 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import android.app.Activity;
import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -69,28 +66,7 @@
SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
- static WorkspaceItemInfo dummyInfo(TaskView view) {
- Task task = view.getTask();
-
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo(){
- /**
- * Helps to log events as {@link LauncherAtom.Task}
- * instead of {@link LauncherAtom.ItemInfo}.
- */
- @Override
- public LauncherAtom.ItemInfo buildProto() {
- return view.buildProto();
- }
- };
- dummyInfo.intent = new Intent();
- ComponentName component = task.getTopComponent();
- dummyInfo.getIntent().setComponent(component);
- dummyInfo.user = UserHandle.of(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
- return dummyInfo;
- }
-
- TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
+ TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
abstract class MultiWindowFactory implements TaskShortcutFactory {
@@ -134,7 +110,7 @@
public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
- super(iconRes, textRes, activity, dummyInfo(taskView));
+ super(iconRes, textRes, activity, taskView.getItemInfo());
mLauncherEvent = launcherEvent;
mHandler = new Handler(Looper.getMainLooper());
mTaskView = taskView;
@@ -220,7 +196,7 @@
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
- mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto());
+ mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.getItemInfo());
}
}
}
@@ -304,7 +280,7 @@
private final TaskView mTaskView;
public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
- super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
mTaskView = tv;
}
@@ -321,15 +297,30 @@
mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
dismissTaskMenuView(mTarget);
mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
- mTaskView.buildProto());
+ mTaskView.getItemInfo());
}
}
TaskShortcutFactory INSTALL = (activity, view) ->
InstantAppResolver.newInstance(activity).isInstantApp(activity,
view.getTask().getTopComponent().getPackageName())
- ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
+ ? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
TaskShortcutFactory WELLBEING = (activity, view) ->
- WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
+
+ TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
+ if (ENABLE_OVERVIEW_ACTIONS.get()) {
+ return tv.getThumbnail().getTaskOverlay()
+ .getScreenshotShortcut(activity, tv.getItemInfo());
+ }
+ return null;
+ };
+
+ TaskShortcutFactory MODAL = (activity, tv) -> {
+ if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
+ return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
+ }
+ return null;
+ };
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 4ca4e4c..37314ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -539,6 +539,8 @@
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
gestureState.updateRunningTask(mGestureState.getRunningTask());
gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
+ gestureState.updatePreviouslyAppearedTaskIds(
+ mGestureState.getPreviouslyAppearedTaskIds());
} else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
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 798e400..534ef7b 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
@@ -607,8 +607,15 @@
}
}
- public boolean isCenterPageTask() {
- return getScrollX() == getScrollForPage(getPageNearestToCenterOfScreen());
+ /**
+ * Whether the Clear All button is hidden or fully visible. Used to determine if center
+ * displayed page is a task or the Clear All button.
+ *
+ * @return True = Clear All button not fully visible, center page is a task. False = Clear All
+ * button fully visible, center page is Clear All button.
+ */
+ public boolean isClearAllHidden() {
+ return mClearAllButton.getAlpha() != 1f;
}
@Override
@@ -620,7 +627,7 @@
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
- if (isCenterPageTask()) {
+ if (isClearAllHidden()) {
LayoutUtils.setViewEnabled(mActionsView, true);
}
if (getNextPage() > 0) {
@@ -1340,7 +1347,7 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.UP, index, compKey);
mActivity.getStatsLogManager().log(
- LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
+ LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.getItemInfo());
}
}
@@ -1904,7 +1911,7 @@
anim.play(depthAnimator);
}
anim.play(progressAnim);
- anim.setDuration(duration).setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
@@ -1923,7 +1930,7 @@
endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
mActivity.getStatsLogManager().log(
- LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
+ LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.getItemInfo());
}
} else {
onTaskLaunched(false);
@@ -2186,6 +2193,11 @@
if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
}
+ // Only show actions view when it's modal for in-place landscape mode.
+ boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
+ && mOrientationState.getTouchRotation() != ROTATION_0;
+ mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
+ LayoutUtils.setViewEnabled(mActionsView, true);
}
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index a371dd2..26fb563 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -448,6 +448,16 @@
}
/**
+ * Returns whether the snapshot is real.
+ */
+ public boolean isRealSnapshot() {
+ if (mThumbnailData == null) {
+ return false;
+ }
+ return mThumbnailData.isRealSnapshot;
+ }
+
+ /**
* Utility class to position the thumbnail in the TaskView
*/
public static class PreviewPositionHelper {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 5176f2c..3b1210e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -40,6 +40,7 @@
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -48,7 +49,6 @@
import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Process;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -61,13 +61,14 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
@@ -213,7 +214,7 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
- mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
+ mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, getItemInfo());
});
mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -226,14 +227,16 @@
/**
* Builds proto for logging
*/
- public LauncherAtom.ItemInfo buildProto() {
+ public WorkspaceItemInfo getItemInfo() {
ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
- LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
- itemBuilder.setIsWork(componentKey.user != Process.myUserHandle());
- itemBuilder.setTask(LauncherAtom.Task.newBuilder()
- .setComponentName(componentKey.componentName.flattenToShortString())
- .setIndex(getRecentsView().indexOfChild(this)));
- return itemBuilder.build();
+ WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+ dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
+ dummyInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+ dummyInfo.user = componentKey.user;
+ dummyInfo.intent = new Intent().setComponent(componentKey.componentName);
+ dummyInfo.title = TaskUtils.getTitle(getContext(), getTask());
+ dummyInfo.screenId = getRecentsView().indexOfChild(this);
+ return dummyInfo;
}
@Override
@@ -425,11 +428,11 @@
}
private boolean showTaskMenu(int action) {
- if (!getRecentsView().isCenterPageTask()) {
+ if (!getRecentsView().isClearAllHidden()) {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
} else {
mMenuView = TaskMenuView.showForTask(this);
- mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
+ mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, getItemInfo());
UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
LauncherLogProto.ItemType.TASK_ICON);
if (mMenuView != null) {
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a27c127..1b82826 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -76,8 +76,8 @@
<!-- Button text to dismiss opt in for fully predicted hotseat -->
<string name="hotseat_edu_dismiss">No thanks</string>
- <!-- action shown to turn off predictions after onboarding -->
- <string name="hotseat_turn_off">Settings</string>
+ <!-- action shown to toggle predictions after onboarding -->
+ <string name="hotseat_prediction_settings">Settings</string>
<!-- tip shown if user has no items in hotseat to migrate -->
<string name="hotseat_auto_enrolled">Most-used apps appear here, and change based on routines</string>
@@ -86,7 +86,9 @@
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
<!-- tip shown when user migrates and predictions are enabled in hotseat -->
- <string name="hotsaet_tip_prediction_enabled">App suggestions Enabled</string>
+ <string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
+ <!-- tip shown when hotseat edu is requested while predicions are disabled -->
+ <string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4874307..d2e0339 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -31,6 +31,7 @@
import android.os.CancellationSignal;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -75,6 +76,7 @@
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
private OverviewActionsView mActionsView;
+ protected HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -305,6 +307,13 @@
return mShelfPeekAnim;
}
+ /**
+ * Returns Prediction controller for hybrid hotseat
+ */
+ public HotseatPredictionController getHotseatPredictionController() {
+ return mHotseatPredictionController;
+ }
+
public void setHintUserWillBeActive() {
addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 188072a..00b5eb9 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -30,6 +30,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
@@ -128,6 +130,7 @@
private ActivityManager.RunningTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
@@ -147,6 +150,7 @@
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
+ mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
mLastStartedTaskId = other.mLastStartedTaskId;
}
@@ -234,6 +238,9 @@
*/
public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
+ if (lastAppearedTaskTarget != null) {
+ mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
+ }
}
/**
@@ -243,6 +250,14 @@
return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
}
+ public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
+ mPreviouslyAppearedTaskIds = previouslyAppearedTaskIds;
+ }
+
+ public Set<Integer> getPreviouslyAppearedTaskIds() {
+ return mPreviouslyAppearedTaskIds;
+ }
+
/**
* Updates the last task that we started via startActivityFromRecents() during this gesture.
*/
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 07aed52..79b38f2 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED;
+import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
@@ -48,15 +49,18 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import androidx.annotation.BinderThread;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.SecureSettingsObserver;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.NavBarPosition;
+import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -112,9 +116,53 @@
}
enableMultipleRegions(false);
}
+
+ @Override
+ public void onActivityRotation(int displayId) {
+ super.onActivityRotation(displayId);
+ // This always gets called before onDisplayInfoChanged() so we know how to process
+ // the rotation in that method. This is done to avoid having a race condition between
+ // the sensor readings and onDisplayInfoChanged() call
+ if (displayId != mDisplayId) {
+ return;
+ }
+
+ mPrioritizeDeviceRotation = true;
+ if (mInOverview) {
+ // reset, launcher must be rotating
+ mExitOverviewRunnable.run();
+ }
+ }
+ };
+
+ private Runnable mExitOverviewRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mInOverview = false;
+ enableMultipleRegions(false);
+ }
};
private OrientationTouchTransformer mOrientationTouchTransformer;
+ /**
+ * Used to listen for when the device rotates into the orientation of the current
+ * foreground app. For example, if a user quickswitches from a portrait to a fixed landscape
+ * app and then rotates rotates the device to match that orientation, this triggers calls to
+ * sysui to adjust the navbar.
+ */
+ private OrientationEventListener mOrientationListener;
+ private int mPreviousRotation = ROTATION_0;
+ /**
+ * This is the configuration of the foreground app or the app that will be in the foreground
+ * once a quickstep gesture finishes.
+ */
+ private int mCurrentAppRotation = -1;
+ /**
+ * This flag is set to true when the device physically changes orientations. When true,
+ * we will always report the current rotation of the foreground app whenever the display
+ * changes, as it would indicate the user's intention to rotate the foreground app.
+ */
+ private boolean mPrioritizeDeviceRotation = false;
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -193,6 +241,26 @@
userSetupObserver.register();
runOnDestroy(userSetupObserver::unregister);
}
+
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int degrees) {
+ int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
+ mPreviousRotation);
+ if (newRotation == mPreviousRotation) {
+ return;
+ }
+
+ mPreviousRotation = newRotation;
+ mPrioritizeDeviceRotation = true;
+
+ if (newRotation == mCurrentAppRotation) {
+ // When user rotates device to the orientation of the foreground app after
+ // quickstepping
+ toggleSecondaryNavBarsForRotation(false);
+ }
+ }
+ };
}
private void setupOrientationSwipeHandler() {
@@ -268,6 +336,18 @@
mNavBarPosition = new NavBarPosition(mMode, info);
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
+ mCurrentAppRotation = mDisplayRotation;
+
+ /* Update nav bars on the following:
+ * a) if we're not expecting quickswitch, this is coming from an activity rotation
+ * b) we launch an app in the orientation that user is already in
+ * c) We're not in overview, since overview will always be portrait (w/o home rotation)
+ */
+ if ((mPrioritizeDeviceRotation
+ || mCurrentAppRotation == mPreviousRotation) // switch to an app of orientation user is in
+ && !mInOverview) {
+ toggleSecondaryNavBarsForRotation(false);
+ }
}
/**
@@ -553,9 +633,13 @@
mOrientationTouchTransformer.transform(event);
}
- void enableMultipleRegions(boolean enable) {
- mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
- notifySysuiForRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+ private void enableMultipleRegions(boolean enable) {
+ toggleSecondaryNavBarsForRotation(enable);
+ if (enable && !TestProtocol.sDisableSensorRotation) {
+ mOrientationListener.enable();
+ } else {
+ mOrientationListener.disable();
+ }
}
private void notifySysuiForRotation(int rotation) {
@@ -581,10 +665,7 @@
// If we're in landscape w/o ever quickswitching, show the navbar in landscape
enableMultipleRegions(true);
}
- activityInterface.onExitOverview(this, () -> {
- mInOverview = false;
- enableMultipleRegions(false);
- });
+ activityInterface.onExitOverview(this, mExitOverviewRunnable);
} else if (endTarget == GestureState.GestureEndTarget.HOME) {
enableMultipleRegions(false);
} else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
@@ -594,6 +675,11 @@
} else {
notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
+
+ // A new gesture is starting, reset the current device rotation
+ // This is done under the assumption that the user won't rotate the phone and then
+ // quickswitch in the old orientation.
+ mPrioritizeDeviceRotation = false;
} else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
if (!mTaskListFrozen) {
// touched nav bar but didn't go anywhere and not quickswitching, do nothing
@@ -603,7 +689,24 @@
}
}
- int getCurrentActiveRotation() {
+ private void notifySysuiOfCurrentRotation(int rotation) {
+ UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
+ .onQuickSwitchToNewTask(rotation));
+ }
+
+ /**
+ * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
+ * notifies system UI of the primary rotation the user is interacting with
+ *
+ * @param enable if {@code true}, this will report to sysUI the navbar of the region the gesture
+ * started in (during ACTION_DOWN), otherwise will report {@param displayRotation}
+ */
+ private void toggleSecondaryNavBarsForRotation(boolean enable) {
+ mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+ }
+
+ public int getCurrentActiveRotation() {
if (!mMode.hasGestures) {
// touch rotation should always match that of display for 3 button
return mDisplayRotation;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2adcfaa..a88ba3c 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -17,6 +17,7 @@
package com.android.quickstep.logging;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
@@ -63,6 +64,9 @@
private static Context sContext;
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
+ // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
+ // from nano to lite, bake constant to prevent robo test failure.
+ private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
public StatsLogCompatManager(Context context) {
@@ -74,7 +78,7 @@
*/
@Override
public void log(EventEnum event) {
- log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
+ log(event, DEFAULT_INSTANCE_ID, null);
}
/**
@@ -82,14 +86,14 @@
*/
@Override
public void log(EventEnum event, InstanceId instanceId) {
- log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
+ log(event, instanceId, null);
}
/**
* Logs an event and accompanying {@link ItemInfo}.
*/
@Override
- public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) {
+ public void log(EventEnum event, @Nullable ItemInfo info) {
log(event, DEFAULT_INSTANCE_ID, info);
}
@@ -98,10 +102,24 @@
*/
@Override
public void log(EventEnum event, InstanceId instanceId,
- @Nullable LauncherAtom.ItemInfo info) {
+ @Nullable ItemInfo info) {
logInternal(event, instanceId, info,
LAUNCHER_UICHANGED__DST_STATE__HOME,
- LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
+ LAUNCHER_UICHANGED__DST_STATE__BACKGROUND,
+ DEFAULT_PAGE_INDEX);
+ }
+
+ /**
+ * Logs a ranking event and accompanying {@link InstanceId} and package name.
+ */
+ @Override
+ public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
+ int position) {
+ SysUiStatsLog.write(SysUiStatsLog.RANKING_SELECTED,
+ rankingEvent.getId() /* event_id = 1; */,
+ packageName /* package_name = 2; */,
+ instanceId.getId() /* instance_id = 3; */,
+ position /* position_picked = 4; */);
}
/**
@@ -110,38 +128,61 @@
*/
@Override
public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
- LauncherAtom.ItemInfo info = LauncherAtom.ItemInfo.getDefaultInstance();
- if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
- || dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
- info = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
- LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
- LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
- )).build();
- }
- logInternal(event, DEFAULT_INSTANCE_ID, info, srcState, dstState);
+ logInternal(event, DEFAULT_INSTANCE_ID, null, srcState, dstState, pageIndex);
}
/**
- * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
+ * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
*/
private void logInternal(EventEnum event, InstanceId instanceId,
- @Nullable LauncherAtom.ItemInfo info, int srcState, int dstState) {
- info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
+ @Nullable ItemInfo info, int srcState, int dstState, int pageIndex) {
- if (IS_VERBOSE) {
- String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() :
- event.getId() + "";
+ LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel,
+ AllAppsList apps) {
+ writeEvent(event, instanceId, info, srcState, dstState, pageIndex,
+ dataModel.folders);
+ }
+ });
+ }
- Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
- ? String.format("\n%s (State:%s->%s) \n%s", name, getStateString(srcState),
- getStateString(dstState), info)
- : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name, instanceId,
- getStateString(srcState), getStateString(dstState), info));
- }
+ private static void writeEvent(EventEnum event, InstanceId instanceId,
+ @Nullable ItemInfo info, int srcState, int dstState, int pageIndex,
+ IntSparseArrayMap<FolderInfo> folders) {
if (!Utilities.ATLEAST_R) {
return;
}
+ LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
+ if (info != null) {
+ if (info.container >= 0) {
+ atomInfo = info.buildProto(folders.get(info.container));
+ } else {
+ atomInfo = info.buildProto();
+ }
+ } else {
+ if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
+ || dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
+ atomInfo = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
+ LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
+ )).build();
+ }
+ }
+
+ if (IS_VERBOSE) {
+ String name = (event instanceof Enum) ? ((Enum) event).name() :
+ event.getId() + "";
+
+ Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
+ ? String.format("\n%s (State:%s->%s) \n%s\n%s", name, getStateString(srcState),
+ getStateString(dstState), info, atomInfo)
+ : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s\n%s", name,
+ getStateString(srcState), getStateString(dstState), instanceId, info,
+ atomInfo));
+ }
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
@@ -151,24 +192,24 @@
null /* launcher extensions, deprecated */,
false /* quickstep_enabled, deprecated */,
event.getId() /* event_id */,
- info.getItemCase().getNumber() /* target_id */,
+ atomInfo.getItemCase().getNumber() /* target_id */,
instanceId.getId() /* instance_id TODO */,
0 /* uid TODO */,
- getPackageName(info) /* package_name */,
- getComponentName(info) /* component_name */,
- getGridX(info, false) /* grid_x */,
- getGridY(info, false) /* grid_y */,
- getPageId(info, false) /* page_id */,
- getGridX(info, true) /* grid_x_parent */,
- getGridY(info, true) /* grid_y_parent */,
- getPageId(info, true) /* page_id_parent */,
- getHierarchy(info) /* hierarchy */,
- info.getIsWork() /* is_work_profile */,
- info.getRank() /* rank */,
- info.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
- info.getFolderIcon().getToLabelState().getNumber() /* toState */,
- info.getFolderIcon().getLabelInfo() /* edittext */,
- info.getFolderIcon().getCardinality() /* cardinality */);
+ getPackageName(atomInfo) /* package_name */,
+ getComponentName(atomInfo) /* component_name */,
+ getGridX(atomInfo, false) /* grid_x */,
+ getGridY(atomInfo, false) /* grid_y */,
+ getPageId(atomInfo, false) /* page_id */,
+ getGridX(atomInfo, true) /* grid_x_parent */,
+ getGridY(atomInfo, true) /* grid_y_parent */,
+ getPageId(atomInfo, true) /* page_id_parent */,
+ getHierarchy(atomInfo) /* hierarchy */,
+ atomInfo.getIsWork() /* is_work_profile */,
+ atomInfo.getRank() /* rank */,
+ atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
+ atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
+ atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
+ getCardinality(atomInfo) /* cardinality */);
}
/**
@@ -232,11 +273,17 @@
getHierarchy(info) /* hierarchy */,
info.getIsWork() /* is_work_profile */,
info.getAttribute().getNumber() /* origin */,
- info.getFolderIcon().getCardinality() /* cardinality */,
+ getCardinality(info) /* cardinality */,
info.getWidget().getSpanX(),
info.getWidget().getSpanY());
}
+ private static int getCardinality(LauncherAtom.ItemInfo info) {
+ return info.getContainerInfo().getContainerCase().equals(PREDICTED_HOTSEAT_CONTAINER)
+ ? info.getContainerInfo().getPredictedHotseatContainer().getCardinality()
+ : info.getFolderIcon().getCardinality();
+ }
+
private static String getPackageName(LauncherAtom.ItemInfo info) {
switch (info.getItemCase()) {
case APPLICATION:
@@ -313,7 +360,7 @@
}
private static String getStateString(int state) {
- switch(state) {
+ switch (state) {
case LAUNCHER_UICHANGED__DST_STATE__BACKGROUND:
return "BACKGROUND";
case LAUNCHER_UICHANGED__DST_STATE__HOME:
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 7e8222c..1abe903 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -31,6 +31,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
@@ -100,6 +101,28 @@
});
}
+ if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ stateManager.addStateListener(new StateListener<LauncherState>() {
+ boolean mFromAllApps = false;
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ mFromAllApps = mLauncher.getStateManager().getCurrentStableState() == ALL_APPS;
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ HotseatPredictionController client = mLauncher.getHotseatPredictionController();
+ if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
+ if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ client.showDiscoveryTip();
+ stateManager.removeStateListener(this);
+ }
+ }
+ }
+ });
+ }
+
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
&& FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
stateManager.addStateListener(new StateListener<LauncherState>() {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 7715cca..8bd2281 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -26,8 +26,8 @@
import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.ContentResolver;
@@ -49,6 +49,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -197,7 +198,7 @@
mPreviousRotation = touchRotation;
if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
- mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
+ mOrientationHandler = PagedOrientationHandler.PORTRAIT;
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
@@ -445,7 +446,8 @@
}
break;
case ROTATION_270:
- if (degrees < (90 - threshold)) {
+ if (degrees < (90 - threshold) ||
+ (degrees > (270 + threshold) && degrees < 360)) {
return ROTATION_0;
}
if (degrees > (90 + threshold) && degrees < 180) {
@@ -468,7 +470,8 @@
if (degrees < (270 - threshold) && degrees > 90) {
return ROTATION_180;
}
- if (degrees > (270 + threshold) && degrees < 360) {
+ if (degrees > (270 + threshold) && degrees < 360
+ || (degrees >= 0 && degrees < threshold)) {
return ROTATION_0;
}
// flip from landscape to seascape
@@ -524,4 +527,15 @@
+ " mFlags=" + mFlags
+ "]";
}
+
+ /**
+ * Returns the device profile based on expected launcher rotation
+ */
+ public DeviceProfile getLauncherDeviceProfile() {
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+ // TODO also check the natural orientation is landscape or portrait
+ return (mLauncherRotation == ROTATION_90 || mLauncherRotation == ROTATION_270)
+ ? idp.landscapeProfile
+ : idp.portraitProfile;
+ }
}
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index 087e45a..0ec9981 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -34,6 +34,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:paddingTop="5dp"
+ android:paddingBottom="5dp"
android:gravity="center"
android:layout_gravity="center_vertical"
android:textColor="@android:color/white"
@@ -58,6 +60,5 @@
android:elevation="2dp"
android:layout_width="10dp"
android:layout_height="8dp"
- android:layout_marginTop="-2dp"
- android:layout_gravity="center_horizontal"/>
+ android:layout_marginTop="-2dp"/>
</merge>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 572615f..cd27a2d 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -174,7 +174,8 @@
targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
if (mIsOpen) {
- performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ getAccessibilityInitialFocusView().performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
}
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
@@ -184,6 +185,11 @@
return null;
}
+ /** Returns the View that Accessibility services should focus on first. */
+ protected View getAccessibilityInitialFocusView() {
+ return this;
+ }
+
/**
* Returns a view matching FloatingViewType
*/
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 268b910..88dbfd6 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -188,7 +188,7 @@
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) {
- getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item.buildProto());
+ getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item);
}
return true;
} catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 208d565..5512654 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -163,6 +163,7 @@
public static final int CONTAINER_SEARCH_RESULTS = -106;
public static final int CONTAINER_SHORTCUTS = -107;
public static final int CONTAINER_SETTINGS = -108;
+ public static final int CONTAINER_TASKSWITCHER = -109;
public static final String containerToString(int container) {
switch (container) {
@@ -250,6 +251,12 @@
public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
/**
+ * Type of the item is recents task.
+ * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
+ */
+ public static final int ITEM_TYPE_TASK = 7;
+
+ /**
* The appWidgetId of the widget
*
* <P>Type: INTEGER</P>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8e33406..bf63788 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -606,6 +606,7 @@
outObj[0] = activityInfo;
return activityInfo.getFullResIcon(appState.getIconCache());
}
+ if (info.getIntent() == null || info.getIntent().getPackage() == null) return null;
List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
.buildRequest(launcher)
.query(ShortcutRequest.ALL);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 32685b0..4198e9f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -418,10 +418,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
dragObject.logInstanceId,
- dragObject.dragSource instanceof Folder
- ? dragObject.originalDragInfo
- .buildProto(((Folder) dragObject.dragSource).mInfo)
- : dragObject.originalDragInfo.buildProto()
+ dragObject.originalDragInfo
);
}
@@ -1342,7 +1339,6 @@
ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
stepAnimator.addUpdateListener(listener);
- stepAnimator.setDuration(config.duration);
stepAnimator.addListener(listener);
animation.add(stepAnimator);
}
@@ -1653,7 +1649,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
d.logInstanceId,
- destInfo.buildProto(null));
+ destInfo);
FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
targetCell[1]);
destInfo.cellX = -1;
@@ -1694,7 +1690,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
- fi.mInfo.buildProto(null));
+ fi.mInfo);
fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
// if the drag started here, we need to remove it from the workspace
@@ -1900,7 +1896,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
- d.dragInfo.buildProto(null));
+ d.dragInfo);
}
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -2441,7 +2437,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
- d.dragInfo.buildProto(null));
+ d.dragInfo);
}
};
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2533,7 +2529,7 @@
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
- d.dragInfo.buildProto(null));
+ d.dragInfo);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index e786ad1..1dd81e8 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -175,7 +175,6 @@
: FAST_OUT_SLOW_IN;
Animator anim = createSpringAnimation(mProgress, targetProgress);
- anim.setDuration(config.duration);
anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
builder.add(anim);
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index afeb341..4195933 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -69,7 +69,7 @@
}
public void add(Animator a, SpringProperty springProperty) {
- mAnim.play(a);
+ mAnim.play(a.setDuration(mDuration));
addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
}
@@ -87,7 +87,7 @@
}
ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
anim.addListener(new AlphaUpdateListener(view));
- anim.setDuration(mDuration).setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
add(anim);
}
@@ -105,7 +105,7 @@
public <T> void addFloat(T target, FloatProperty<T> property, float from, float to,
TimeInterpolator interpolator) {
Animator anim = ObjectAnimator.ofFloat(target, property, from, to);
- anim.setDuration(mDuration).setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
add(anim);
}
@@ -116,7 +116,7 @@
return;
}
Animator anim = ObjectAnimator.ofInt(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
add(anim);
}
@@ -125,7 +125,7 @@
*/
public void addOnFrameCallback(Runnable runnable) {
if (mProgressAnimator == null) {
- mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
+ mProgressAnimator = ValueAnimator.ofFloat(0, 1);
}
mProgressAnimator.addUpdateListener(anim -> runnable.run());
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index fdf0ea4..530010e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -745,6 +745,11 @@
: getContext().getString(R.string.folder_closed));
}
+ @Override
+ protected View getAccessibilityInitialFocusView() {
+ return mContent.getFirstItem();
+ }
+
private void closeComplete(boolean wasAnimated) {
// TODO: Clear all active animations.
DragLayer parent = (DragLayer) getParent();
@@ -1333,7 +1338,7 @@
d.stateAnnouncer.completeAction(R.string.item_moved);
}
mStatsLogManager
- .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
+ .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo);
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1438,7 +1443,7 @@
if (hasFocus) {
startEditingFolderName();
} else {
- mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
+ mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo);
logFolderLabelState();
mFolderName.dispatchBackKey();
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 153d6bc..098ce50 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -450,7 +450,7 @@
}
mInfo.setTitle(nameInfos[0].getLabel());
StatsLogManager.newInstance(getContext())
- .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
+ .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo);
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index f0a9efb..e95c062 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -21,8 +21,8 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom.ItemInfo;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ResourceBasedOverride;
/**
@@ -127,12 +127,45 @@
LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP(522),
@UiEvent(doc = "User is shown All Apps education view.")
- LAUNCHER_ALL_APPS_EDU_SHOWN(523);
+ LAUNCHER_ALL_APPS_EDU_SHOWN(523),
+
+ @UiEvent(doc = "User opened a folder.")
+ LAUNCHER_FOLDER_OPEN(551),
+
+ @UiEvent(doc = "Hotseat education half sheet seen")
+ LAUNCHER_HOTSEAT_EDU_SEEN(479),
+
+ @UiEvent(doc = "Hotseat migration accepted")
+ LAUNCHER_HOTSEAT_EDU_ACCEPT(480),
+
+ @UiEvent(doc = "Hotseat migration denied")
+ LAUNCHER_HOTSEAT_EDU_DENY(481),
+
+ @UiEvent(doc = "Hotseat education tip shown")
+ LAUNCHER_HOTSEAT_EDU_ONLY_TIP(482);
+ // ADD MORE
+ private final int mId;
+
+ LauncherEvent(int id) {
+ mId = id;
+ }
+
+ public int getId() {
+ return mId;
+ }
+ }
+
+ /**
+ * Launcher specific ranking related events.
+ */
+ public enum LauncherRankingEvent implements EventEnum {
+
+ UNKNOWN(0);
// ADD MORE
private final int mId;
- LauncherEvent(int id) {
+ LauncherRankingEvent(int id) {
mId = id;
}
@@ -158,30 +191,55 @@
}
/**
- * Logs a {@link EventEnum}.
+ * Logs an event.
+ *
+ * @param event an enum implementing EventEnum interface.
*/
public void log(EventEnum event) {
}
/**
- * Logs an event and accompanying {@link InstanceId}.
+ * Logs an event.
+ *
+ * @param event an enum implementing EventEnum interface.
+ * @param instanceId an identifier obtained from an InstanceIdSequence.
*/
public void log(EventEnum event, InstanceId instanceId) {
}
/**
- * Logs an event and accompanying {@link ItemInfo}.
+ * Logs an event.
+ *
+ * @param event an enum implementing EventEnum interface.
+ * @param itemInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
}
/**
- * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
+ * Logs an event.
+ *
+ * @param event an enum implementing EventEnum interface.
+ * @param instanceId an identifier obtained from an InstanceIdSequence.
+ * @param itemInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
}
/**
+ * Log an event with ranked-choice information along with package. Does nothing if event.getId()
+ * <= 0.
+ *
+ * @param rankingEvent an enum implementing EventEnum interface.
+ * @param instanceId An identifier obtained from an InstanceIdSequence.
+ * @param packageName the package name of the relevant app, if known (null otherwise).
+ * @param position the position picked.
+ */
+ public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
+ int position) {
+ }
+
+ /**
* Logs an event and accompanying {@link LauncherState}s. If either of the state refers
* to workspace state, then use pageIndex to pass in index of workspace.
*/
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 7818ff5..e094cab 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -48,7 +48,6 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.userevent.LauncherLogProto;
@@ -143,14 +142,6 @@
fillIntentInfo(itemTarget, intent, userHandle);
}
LauncherEvent event = newLauncherEvent(action, targets);
- ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
- if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- final String pkg = info.getTargetComponent() != null
- ? info.getTargetComponent().getPackageName() : "unknown";
- FileLog.d(TAG, "appLaunch: packageName:" + pkg
- + ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
- userHandle)) + ",launchLocation:" + info.container);
- }
dispatchUserEvent(event, intent);
mAppOrTaskLaunch = true;
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index a5d798b..25a2c69 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -59,7 +59,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.stream.Collectors;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -248,12 +247,25 @@
/** Return what's in the src but not in the dest */
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
- Set<String> destSet = dest.parallelStream().map(DbEntry::getIntentStr).collect(
- Collectors.toSet());
+ Set<String> destIntentSet = new HashSet<>();
+ Set<Set<String>> destFolderIntentSet = new HashSet<>();
+ for (DbEntry entry : dest) {
+ if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ destFolderIntentSet.add(entry.mFolderItems.keySet());
+ } else {
+ destIntentSet.add(entry.mIntent);
+ }
+ }
List<DbEntry> diff = new ArrayList<>();
for (DbEntry entry : src) {
- if (!destSet.contains(entry.mIntent)) {
- diff.add(entry);
+ if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ if (!destFolderIntentSet.contains(entry.mFolderItems.keySet())) {
+ diff.add(entry);
+ }
+ } else {
+ if (!destIntentSet.contains(entry.mIntent)) {
+ diff.add(entry);
+ }
}
}
return diff;
@@ -384,7 +396,7 @@
* to speed up the search.
*/
private boolean findPlacement(DbEntry entry) {
- for (int y = mNextStartY; y >= 0; y--) {
+ for (int y = mNextStartY; y > 0; y--) {
for (int x = mNextStartX; x < mTrgX; x++) {
boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 8dcdec1..66c3cbb 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -24,11 +24,13 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
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;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
import android.content.ComponentName;
@@ -49,6 +51,7 @@
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
+import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.util.ContentWriter;
import java.util.Optional;
@@ -298,6 +301,12 @@
.setSpanX(spanX)
.setSpanY(spanY));
break;
+ case ITEM_TYPE_TASK:
+ itemBuilder
+ .setTask(LauncherAtom.Task.newBuilder()
+ .setComponentName(getTargetComponent().flattenToShortString())
+ .setIndex(screenId));
+ break;
default:
break;
}
@@ -337,10 +346,13 @@
ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
- case CONTAINER_HOTSEAT_PREDICTION:
return ContainerInfo.newBuilder()
.setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId))
.build();
+ case CONTAINER_HOTSEAT_PREDICTION:
+ return ContainerInfo.newBuilder().setPredictedHotseatContainer(
+ LauncherAtom.PredictedHotseatContainer.newBuilder().setIndex(screenId))
+ .build();
case CONTAINER_DESKTOP:
return ContainerInfo.newBuilder()
.setWorkspace(
@@ -375,6 +387,11 @@
return ContainerInfo.newBuilder()
.setSettingsContainer(SettingsContainer.getDefaultInstance())
.build();
+ case CONTAINER_TASKSWITCHER:
+ return ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
+ .build();
+
}
return ContainerInfo.getDefaultInstance();
}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index fa1bdfb..f1b63f2 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -108,7 +108,7 @@
intent.send(null, 0, null, null, null, null, activityOptions);
launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
launcher.getStatsLogManager()
- .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo.buildProto());
+ .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5b0c388..d5b32fc 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -389,6 +389,11 @@
return Pair.create(this, "");
}
+ @Override
+ protected View getAccessibilityInitialFocusView() {
+ return getChildCount() > 0 ? getChildAt(0) : this;
+ }
+
private void animateOpen() {
setVisibility(View.VISIBLE);
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 58251e8..8e60c27 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -78,7 +78,7 @@
public void onClick(View view) {
AbstractFloatingView.closeAllOpenViews(mTarget);
mTarget.getStatsLogManager()
- .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo.buildProto());
+ .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo);
final String actionIdentity = mAction.getTitle() + ", "
+ mItemInfo.getTargetComponent().getPackageName();
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index ea8caf5..59d24de 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -119,9 +119,7 @@
widgetsBottomSheet.populateAndShow(mItemInfo);
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.WIDGETS_BUTTON, view);
- // TODO(thiruram): Fix missing container info when item is inside folder.
- mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP,
- mItemInfo.buildProto());
+ mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, mItemInfo);
}
}
@@ -142,9 +140,8 @@
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
- // TODO(thiruram): Fix missing container info when item is inside folder.
mTarget.getStatsLogManager()
- .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo.buildProto());
+ .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo);
}
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 2c21609..171c5ee 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -310,6 +310,9 @@
}
protected void updateProgress(float fraction) {
+ if (mCurrentAnimation == null) {
+ return;
+ }
mCurrentAnimation.setPlayFraction(fraction);
if (mAtomicComponentsController != null) {
// Make sure we don't divide by 0, and have at least a small runway.
diff --git a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java b/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
deleted file mode 100644
index db5c659..0000000
--- a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.touch;
-
-import android.graphics.RectF;
-import android.view.Surface;
-import android.widget.LinearLayout;
-
-public class HomeRotatedPageHandler extends PortraitPagedViewHandler {
- @Override
- public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
- if (launcherRotation == Surface.ROTATION_0) {
- super.offsetTaskRect(rect, value, displayRotation, launcherRotation);
- } else if (launcherRotation == Surface.ROTATION_90) {
- if (displayRotation == Surface.ROTATION_0) {
- rect.offset(0, value);
- } else if (displayRotation == Surface.ROTATION_90) {
- rect.offset(value, 0);
- } else if (displayRotation == Surface.ROTATION_180) {
- rect.offset(-value, 0);
- } else {
- rect.offset(-value, 0);
- }
- } else if (launcherRotation == Surface.ROTATION_270) {
- if (displayRotation == Surface.ROTATION_0) {
- rect.offset(0, -value);
- } else if (displayRotation == Surface.ROTATION_90) {
- rect.offset(value, 0);
- } else if (displayRotation == Surface.ROTATION_180) {
- rect.offset(0, -value);
- } else {
- rect.offset(value, 0);
- }
- } // TODO (b/149609488) handle 180 case as well
- }
-
- @Override
- public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
- return taskMenuLayout.getOrientation();
- }
-}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 6abca76..de16941 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
@@ -45,6 +46,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -111,6 +113,7 @@
if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
folder.animateOpen();
+ StatsLogManager.newInstance(v.getContext()).log(LAUNCHER_FOLDER_OPEN, folder.mInfo);
}
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index d02c731..48c7734 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -181,19 +181,6 @@
}
@Override
- public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
- if (displayRotation == Surface.ROTATION_0) {
- rect.offset(0, value);
- } else if (displayRotation == Surface.ROTATION_90) {
- rect.offset(value, 0);
- } else if (displayRotation == Surface.ROTATION_180) {
- rect.offset(0, -value);
- } else {
- rect.offset(-value, 0);
- }
- }
-
- @Override
public int getChildStart(View view) {
return view.getTop();
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 2e0268d..65b1a7a 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -43,7 +43,6 @@
PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
- PagedOrientationHandler HOME_ROTATED = new HomeRotatedPageHandler();
interface Int2DAction<T> {
void call(T target, int x, int y);
@@ -82,7 +81,6 @@
boolean getRecentsRtlSetting(Resources resources);
float getDegreesRotated();
int getRotation();
- void offsetTaskRect(RectF rect, float value, int delta, int launcherRotation);
int getPrimaryValue(int x, int y);
int getSecondaryValue(int x, int y);
void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 2fc7a9f..79e5c87 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -179,19 +179,6 @@
}
@Override
- public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
- if (displayRotation == Surface.ROTATION_0) {
- rect.offset(value, 0);
- } else if (displayRotation == Surface.ROTATION_90) {
- rect.offset(0, -value);
- } else if (displayRotation == Surface.ROTATION_180) {
- rect.offset(-value, 0);
- } else {
- rect.offset(0, value);
- }
- }
-
- @Override
public int getChildStart(View view) {
return view.getLeft();
}
@@ -250,7 +237,7 @@
@Override
public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
- return LinearLayout.VERTICAL;
+ return taskMenuLayout.getOrientation();
}
@Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 4c1700e..d5ae2dc 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -18,7 +18,6 @@
import android.content.res.Resources;
import android.graphics.PointF;
-import android.graphics.RectF;
import android.view.Surface;
import android.view.View;
@@ -42,19 +41,6 @@
}
@Override
- public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
- if (displayRotation == Surface.ROTATION_0) {
- rect.offset(0, value);
- } else if (displayRotation == Surface.ROTATION_90) {
- rect.offset(value, 0);
- } else if (displayRotation == Surface.ROTATION_180) {
- rect.offset(0, -value);
- } else {
- rect.offset(-value, 0);
- }
- }
-
- @Override
public float getDegreesRotated() {
return 270;
}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 1620289..90a1c82 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -37,13 +37,14 @@
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
+ public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
/**
* Events that either have happened or have not (booleans).
*/
@StringDef(value = {
HOME_BOUNCE_SEEN,
- SHELF_BOUNCE_SEEN,
+ SHELF_BOUNCE_SEEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
@@ -55,6 +56,7 @@
HOME_BOUNCE_COUNT,
SHELF_BOUNCE_COUNT,
ALL_APPS_COUNT,
+ HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventCountKey {}
@@ -65,6 +67,7 @@
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
+ maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index a7575d1..b4a6b14 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -125,11 +125,35 @@
* Show Tip with specified string and Y location
*/
public ArrowTipView show(String text, int top) {
+ return show(text, Gravity.CENTER_HORIZONTAL, 0, top);
+ }
+
+ /**
+ * Show the ArrowTipView (tooltip) center, start, or end aligned.
+ *
+ * @param text The text to be shown in the tooltip.
+ * @param gravity The gravity aligns the tooltip center, start, or end.
+ * @param arrowMarginStart The margin from start to place arrow (ignored if center)
+ * @param top The Y coordinate of the bottom of tooltip.
+ * @return The tooltip.
+ */
+ public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
((TextView) findViewById(R.id.text)).setText(text);
- mActivity.getDragLayer().addView(this);
+ ViewGroup parent = mActivity.getDragLayer();
+ parent.addView(this);
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
- params.gravity = Gravity.CENTER_HORIZONTAL;
+ params.gravity = gravity;
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) findViewById(
+ R.id.arrow).getLayoutParams();
+ lp.gravity = gravity;
+ if (gravity == Gravity.END) {
+ lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart);
+ } else if (gravity == Gravity.START) {
+ lp.setMarginStart(arrowMarginStart);
+ }
+ requestLayout();
+
params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - getHeight()));