Merge "Removing debug requests from non-debug test info handlers" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 26ed88a..d1185bd 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -38,7 +38,7 @@
optional ContainerInfo container_info = 7;
// Stores the origin of the Item
- optional Origin source = 8;
+ optional Attribute attribute = 8;
}
// Represents various launcher surface where items are placed.
@@ -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,7 +83,10 @@
message SettingsContainer {
}
-enum Origin {
+message TaskSwitcherContainer {
+}
+
+enum Attribute {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
BACKUP_RESTORE = 2; // icon layout restored from backup
@@ -91,6 +96,8 @@
ADD_TO_HOMESCREEN = 6; // play install + launcher home setting
ALLAPPS_PREDICTION = 7; // from prediction bar in all apps container
HOTSEAT_PREDICTION = 8; // from prediction bar in hotseat container
+ SUGGESTED_LABEL = 9; // folder icon's label was suggested
+ MANUAL_LABEL = 10; // folder icon's label was manually edited
}
// Main app icons
@@ -149,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/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 527bfc3..60afddb 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -52,7 +52,7 @@
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
- android:taskAffinity="${packageName}.launcher"
+ android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index b2286f1..e49f2ec 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -22,11 +22,16 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
+ <permission
+ android:name="${packageName}.permission.HOTSEAT_EDU"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signatureOrSystem" />
+
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
-
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
@@ -99,13 +104,24 @@
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
- android:taskAffinity="${packageName}.launcher"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity
+ android:name=".hybridhotseat.HotseatEduActivity"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:noHistory="true"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:permission="${packageName}.permission.HOTSEAT_EDU">
+ <intent-filter>
+ <action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index b9621e4..36c9b00 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -72,33 +72,42 @@
android:layout_height="0dp"
launcher:containerType="hotseat" />
- <FrameLayout
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingTop="8dp"
android:paddingRight="@dimen/bottom_sheet_edu_padding">
- <Button
- android:id="@+id/turn_predictions_on"
- android:layout_width="wrap_content"
+ <FrameLayout
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/hotseat_edu_accept"
- android:textAlignment="textEnd"
- android:textColor="@android:color/white" />
-
- <Button
- android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight=".4">
+ <Button
+ android:id="@+id/no_thanks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/hotseat_edu_dismiss"
+ android:textColor="@android:color/white"/>
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:id="@+id/no_thanks"
- android:text="@string/hotseat_edu_dismiss"
- android:layout_gravity="start"
- android:background="?android:attr/selectableItemBackground"
- android:textColor="@android:color/white" />
+ android:layout_gravity="center_vertical"
+ android:layout_weight=".6">
+ <Button
+ android:id="@+id/turn_predictions_on"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="end"
+ android:text="@string/hotseat_edu_accept"
+ android:textColor="@android:color/white"/>
+ </FrameLayout>
- </FrameLayout>
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
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..5e54cd2 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
@@ -16,6 +16,9 @@
package com.android.launcher3.appprediction;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+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.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -25,6 +28,7 @@
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
@@ -48,6 +52,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
import java.util.stream.IntStream;
/**
@@ -302,6 +308,26 @@
}
/**
+ * Returns ranking info for the app within all apps prediction.
+ * Only applicable when {@link ItemInfo#itemType} is one of the followings:
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+ */
+ public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) {
+ Optional<ComponentKey> componentKey = Optional.ofNullable(itemInfo)
+ .filter(item -> item.itemType == ITEM_TYPE_APPLICATION
+ || item.itemType == ITEM_TYPE_SHORTCUT
+ || item.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+ .map(ItemInfo::getTargetComponent)
+ .map(componentName -> new ComponentKey(componentName, itemInfo.user));
+
+ return componentKey.map(key -> IntStream.range(0, getCurrentState().apps.size())
+ .filter(index -> key.equals(getCurrentState().apps.get(index).getComponentKey()))
+ .findFirst()).orElseGet(OptionalInt::empty);
+ }
+
+ /**
* Fill in predicted_rank field based on app prediction.
* Only applicable when {@link ItemInfo#itemType} is one of the followings:
* {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
@@ -310,6 +336,7 @@
*/
public static void fillInPredictedRank(
@NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
+
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/HotseatEduActivity.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
new file mode 100644
index 0000000..c968de9
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.ActivityTracker;
+
+/**
+ * Proxy activity to return user to home screen and show halfsheet education
+ */
+public class HotseatEduActivity extends Activity {
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(getPackageName())
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ new HotseatActivityTracker<>().addToIntent(homeIntent);
+ startActivity(homeIntent);
+ finish();
+ }
+
+ static class HotseatActivityTracker<T extends QuickstepLauncher> implements
+ ActivityTracker.SchedulerCallback {
+
+ @Override
+ public boolean init(BaseActivity activity, boolean alreadyOnHome) {
+ QuickstepLauncher launcher = (QuickstepLauncher) activity;
+ if (launcher != null && launcher.getHotseatPredictionController() != null) {
+ launcher.getHotseatPredictionController().showEdu();
+ }
+ return false;
+ }
+
+ }
+}
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..c1bf2fd 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(getSettingsIntent()));
}
/**
@@ -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(getSettingsIntent()));
} 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().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
finishOnboarding();
}
}
@@ -280,5 +281,9 @@
mActiveDialog.setHotseatEduController(this);
mActiveDialog.show(mPredictedApps);
}
+
+ static Intent getSettingsIntent() {
+ return new Intent(SETTINGS_ACTION).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
}
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..2b3f395 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().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
}
private void onDismiss(View v) {
mHotseatEduController.showDimissTip();
mHotseatEduController.finishOnboarding();
- logUserAction(false, -1);
+ mLauncher.getStatsLogManager().logger().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().logger().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..7334d80 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,9 @@
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -27,7 +30,9 @@
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.content.ComponentName;
+import android.os.Process;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.ViewGroup;
@@ -50,7 +55,9 @@
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.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
+import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -64,6 +71,9 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -107,17 +117,24 @@
private boolean mIsCacheEmpty;
private boolean mIsDestroyed = false;
- private HotseatEduController mHotseatEduController;
-
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
private final View.OnLongClickListener mPredictionLongClickListener = v -> {
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (mLauncher.getWorkspace().isSwitchingState()) return false;
+ if (!mLauncher.getOnboardingPrefs().getBoolean(
+ OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(getSettingsIntent()));
+ mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
+ mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ return true;
+ }
// Start the drag
mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
- return false;
+ return true;
};
public HotseatPredictionController(Launcher launcher) {
@@ -146,11 +163,46 @@
}
/**
- * 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();
+ mLauncher.getStateManager().goToState(NORMAL, true, () -> {
+ 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(getSettingsIntent()));
+ } else if (isEduSeen() || getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
+ showDiscoveryTip();
+ } 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.
+ */
+ private void showDiscoveryTip() {
+ if (getPredictedIcons().isEmpty()) {
+ 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(getSettingsIntent()));
+ }
+ }
+
+ /**
+ * Returns if hotseat client has predictions
+ */
+ public boolean hasPredictions() {
+ return !mComponentKeyMappers.isEmpty();
}
@Override
@@ -250,10 +302,6 @@
if (mAppPredictor != null) {
mAppPredictor.destroy();
}
- if (mHotseatEduController != null) {
- mHotseatEduController.destroy();
- mHotseatEduController = null;
- }
}
/**
@@ -299,20 +347,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);
@@ -324,6 +372,7 @@
updateDependencies();
bindItems(items, false, null);
}
+
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
if (appTargets.isEmpty()) {
@@ -347,12 +396,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);
}
@@ -602,6 +650,49 @@
mHotseat.fillInLogContainerData(childInfo, child, parents);
}
+ /**
+ * Logs rank info based on current list of predicted items
+ */
+ public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
+ if (Utilities.IS_DEBUG_DEVICE) {
+ final String pkg = itemInfo.getTargetComponent() != null
+ ? itemInfo.getTargetComponent().getPackageName() : "unknown";
+ HotseatFileLog.INSTANCE.get(mLauncher).log("UserEvent",
+ "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
+ && !Process.myUserHandle().equals(itemInfo.user))
+ + ",launchLocation:" + itemInfo.container);
+ }
+
+ final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
+
+ final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
+ OptionalInt rank = IntStream.range(0, predictedApps.size())
+ .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
+ .findFirst();
+ if (!rank.isPresent()) {
+ return;
+ }
+
+ int cardinality = 0;
+ for (PredictedAppIcon icon : getPredictedIcons()) {
+ ItemInfo info = (ItemInfo) icon.getTag();
+ cardinality |= 1 << info.screenId;
+ }
+
+ PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
+ containerBuilder.setCardinality(cardinality);
+ if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ containerBuilder.setIndex(rank.getAsInt());
+ }
+ mLauncher.getStatsLogManager().logger()
+ .withInstanceId(instanceId)
+ .withRank(rank.getAsInt())
+ .withContainerInfo(ContainerInfo.newBuilder()
+ .setPredictedHotseatContainer(containerBuilder)
+ .build())
+ .log(LAUNCHER_HOTSEAT_RANKED);
+ }
+
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
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..597c17b 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.095f;
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..7d86cea 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
@@ -21,6 +21,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
@@ -42,10 +43,12 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.hybridhotseat.HotseatEduController;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -78,6 +81,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.OptionalInt;
import java.util.stream.Stream;
public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -88,28 +92,33 @@
*/
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) {
super.onCreate(savedInstanceState);
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- mHotseatPredictionController = new HotseatPredictionController(this);
+ if (mHotseatPredictionController != null) {
mHotseatPredictionController.createPredictor();
}
}
@Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- if (HotseatEduController.HOTSEAT_EDU_ACTION.equals(intent.getAction())
- && mHotseatPredictionController != null) {
- boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags()
- & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
- != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- getStateManager().goToState(NORMAL, alreadyOnHome, () -> {
- mHotseatPredictionController.showEdu();
- });
+ protected void setupViews() {
+ super.setupViews();
+ if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
+ mHotseatPredictionController = new HotseatPredictionController(this);
+ }
+ }
+
+ @Override
+ protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+ StatsLogger logger = getStatsLogManager()
+ .logger().withItemInfo(info).withInstanceId(instanceId);
+ OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info);
+ allAppsRank.ifPresent(logger::withRank);
+ logger.log(LAUNCHER_APP_LAUNCH_TAP);
+
+ if (mHotseatPredictionController != null) {
+ mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
}
}
@@ -169,13 +178,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/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index d5b0687..fc0dcd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -20,6 +20,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.views.RecentsView;
@@ -56,6 +57,17 @@
return 1.0f;
}
+ @Override
+ public void onBackPressed(Launcher launcher) {
+ launcher.getStateManager().goToState(LauncherState.OVERVIEW);
+ RecentsView recentsView = launcher.<RecentsView>getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.resetModalVisuals();
+ } else {
+ super.onBackPressed(launcher);
+ }
+ }
+
public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
Rect out = new Rect();
activity.<RecentsView>getOverviewPanel().getTaskSize(out);
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..a0af797 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
@@ -24,6 +24,7 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
@@ -90,6 +91,10 @@
protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
+ MY_ANIM_COUNT;
+ // Due to use of physics, duration may differ between devices so we need to calculate and
+ // cache the value.
+ private int mHintToNormalDuration = -1;
+
public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
@@ -156,6 +161,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 +216,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()
@@ -219,6 +226,14 @@
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+ } else if (fromState == HINT_STATE && toState == NORMAL) {
+ config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
+ if (mHintToNormalDuration == -1) {
+ ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
+ toState.getWorkspaceScaleAndTranslation(mActivity).scale);
+ mHintToNormalDuration = (int) va.getDuration();
+ }
+ config.duration = Math.max(config.duration, mHintToNormalDuration);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 39bbfb9..a35e13a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -15,10 +15,13 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
@@ -112,7 +115,8 @@
}
return true;
}
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
+ if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED,
"NavBarToHomeTouchController.canInterceptTouch true 2 "
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 966e25b..9316938 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -24,7 +24,9 @@
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.PointF;
import android.util.Log;
@@ -34,6 +36,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
@@ -59,9 +62,14 @@
private boolean mDidTouchStartInNavBar;
private boolean mReachedOverview;
+ private boolean mIsOverviewRehidden;
+ private boolean mIsHomeStaggeredAnimFinished;
// The last recorded displacement before we reached overview.
private PointF mStartDisplacement = new PointF();
+ // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
+ private ObjectAnimator mNormalToHintOverviewScrimAnimator;
+
public NoButtonNavbarToOverviewTouchController(Launcher l) {
super(l);
mRecentsView = l.getOverviewPanel();
@@ -107,11 +115,31 @@
@Override
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
-
+ if (mFromState == NORMAL && mToState == HINT_STATE) {
+ mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofFloat(
+ mLauncher.getDragLayer().getOverviewScrim(),
+ OverviewScrim.SCRIM_PROGRESS,
+ mFromState.getOverviewScrimAlpha(mLauncher),
+ mToState.getOverviewScrimAlpha(mLauncher));
+ }
mReachedOverview = false;
}
@Override
+ protected void updateProgress(float fraction) {
+ super.updateProgress(fraction);
+ if (mNormalToHintOverviewScrimAnimator != null) {
+ mNormalToHintOverviewScrimAnimator.setCurrentFraction(fraction);
+ }
+ }
+
+ @Override
+ public void onDragEnd(float velocity) {
+ super.onDragEnd(velocity);
+ mNormalToHintOverviewScrimAnimator = null;
+ }
+
+ @Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity,
@@ -129,6 +157,7 @@
if (mCurrentAnimation == null) {
return;
}
+ mNormalToHintOverviewScrimAnimator = null;
mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
mReachedOverview = true;
@@ -144,6 +173,13 @@
}
}
+ // Used if flinging back to home after reaching overview
+ private void maybeSwipeInteractionToHomeComplete() {
+ if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) {
+ onSwipeInteractionCompleted(NORMAL, Touch.FLING);
+ }
+ }
+
@Override
protected boolean handlingOverviewAnim() {
return mDidTouchStartInNavBar && super.handlingOverviewAnim();
@@ -180,9 +216,17 @@
stateManager.goToState(NORMAL, true,
() -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
} else {
+ mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false;
+
StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
mLauncher, velocity, false /* animateOverviewScrim */);
- staggeredWorkspaceAnim.start();
+ staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mIsHomeStaggeredAnimFinished = true;
+ maybeSwipeInteractionToHomeComplete();
+ }
+ }).start();
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
stateManager.cancelAnimation();
@@ -191,8 +235,10 @@
config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
AnimatorSet anim = stateManager.createAtomicAnimation(
stateManager.getState(), NORMAL, config);
- anim.addListener(AnimationSuccessListener.forRunnable(
- () -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE)));
+ anim.addListener(AnimationSuccessListener.forRunnable(() -> {
+ mIsOverviewRehidden = true;
+ maybeSwipeInteractionToHomeComplete();
+ }));
anim.start();
}
}
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..9db6576 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() {
@@ -122,6 +121,10 @@
});
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ // Wait until the first scroll event before applying scroll to taskViewSimulator.
+ // Since, by default the current/running task already centered, this ensures that we
+ // do not move the running task, in case RecentsView has not yet laid out completely.
+ mRecentsViewScrollLinked = true;
if (moveWindowWithRecentsScroll()) {
updateFinalShift();
}
@@ -129,7 +132,6 @@
runOnRecentsAnimationStart(() ->
mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
mRecentsAnimationTargets));
- mRecentsViewScrollLinked = true;
}
protected void startNewTask(Consumer<Boolean> resultCallback) {
@@ -144,13 +146,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 +174,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,26 +208,30 @@
mRecentsAnimationController = recentsAnimationController;
mRecentsAnimationTargets = targets;
mTransformParams.setTargetSet(mRecentsAnimationTargets);
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
mGestureState.getRunningTaskId());
- if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
- Rect overviewStackBounds = mActivityInterface
- .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
- dp = dp.getMultiWindowProfile(mContext,
- new WindowBounds(overviewStackBounds, targets.homeContentInsets));
- } else {
- // If we are not in multi-window mode, home insets should be same as system insets.
- dp = dp.copy(mContext);
- }
- dp.updateInsets(targets.homeContentInsets);
- dp.updateIsSeascape(mContext);
if (runningTaskTarget != null) {
mTaskViewSimulator.setPreview(runningTaskTarget);
}
- initTransitionEndpoints(dp);
+ // Only initialize the device profile, if it has not been initialized before, as in some
+ // configurations targets.homeContentInsets may not be correct.
+ if (mActivity == null) {
+ DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
+ if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+ Rect overviewStackBounds = mActivityInterface
+ .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+ dp = dp.getMultiWindowProfile(mContext,
+ new WindowBounds(overviewStackBounds, targets.homeContentInsets));
+ } else {
+ // If we are not in multi-window mode, home insets should be same as system insets.
+ dp = dp.copy(mContext);
+ }
+ dp.updateInsets(targets.homeContentInsets);
+ dp.updateIsSeascape(mContext);
+ initTransitionEndpoints(dp);
+ }
// Notify when the animation starts
if (!mRecentsAnimationStartCallbacks.isEmpty()) {
@@ -300,8 +307,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..e825c5f 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.
@@ -128,7 +131,7 @@
private static final int STATE_CAPTURE_SCREENSHOT =
getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
- private static final int STATE_SCREENSHOT_CAPTURED =
+ protected static final int STATE_SCREENSHOT_CAPTURED =
getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
private static final int STATE_SCREENSHOT_VIEW_SHOWN =
getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
@@ -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;
}
@@ -1166,7 +1192,7 @@
mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
}
- private void switchToScreenshot() {
+ protected void switchToScreenshot() {
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 96913c6..fc7a119 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -111,6 +111,16 @@
}
@Override
+ protected void switchToScreenshot() {
+ if (mRunningOverHome) {
+ // When the current task is home, then we don't need to capture anything
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else {
+ super.switchToScreenshot();
+ }
+ }
+
+ @Override
protected void notifyGestureAnimationStartToRecents() {
if (mRunningOverHome) {
mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 6751723..edefbe1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -120,9 +120,7 @@
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
- ((RecentsView) getCreatedActivity().getOverviewPanel())
- .setLayoutRotation(deviceState.getCurrentActiveRotation(),
- deviceState.getDisplayRotation());
+ notifyRecentsOfOrientation(deviceState);
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
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 1fc7aa4..6f4d34c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -43,7 +43,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 3cfff7e..ed07062 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,20 +16,31 @@
package com.android.quickstep;
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static android.view.Surface.ROTATION_0;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
+
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.Build;
+import android.view.View;
import android.widget.Toast;
+import androidx.annotation.RequiresApi;
+
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;
@@ -54,6 +65,28 @@
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) {
+ // Add screenshot action to task menu.
+ SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
+ .getShortcut(activity, taskView);
+ if (screenshotShortcut != null) {
+ shortcuts.add(screenshotShortcut);
+ }
+
+ // Add modal action only if display orientation is the same as the device orientation.
+ if (orientedState.getDisplayRotation() == ROTATION_0) {
+ SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
+ .getShortcut(activity, taskView);
+ if (modalShortcut != null) {
+ shortcuts.add(modalShortcut);
+ }
+ }
+ }
return shortcuts;
}
@@ -90,10 +123,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() {
@@ -107,33 +144,42 @@
/**
* 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);
+ public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
+ boolean rotated) {
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ mActionsView.updateDisabledFlags(DISABLED_ROTATED, rotated);
+
getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override
public void onShare() {
if (isAllowedByPolicy) {
- imageApi.startShareActivity();
+ mImageApi.startShareActivity();
} else {
showBlockedByPolicyMessage();
}
}
+ @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.
@@ -142,6 +188,26 @@
}
/**
+ * Called when the system wants to reset the modal visuals.
+ */
+ public void resetModalVisuals() {
+ }
+
+ /**
+ * 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.
@@ -159,9 +225,9 @@
*
* @return the insets in screen coordinates.
*/
+ @RequiresApi(api = Build.VERSION_CODES.Q)
public Insets getTaskSnapshotInsets() {
- // TODO: return the real insets
- return Insets.of(0, 0, 0, 0);
+ return mThumbnailView.getScaledInsets();
}
private void showBlockedByPolicyMessage() {
@@ -170,6 +236,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..ff051b6 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,8 @@
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
- mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto());
+ mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+ .log(mLauncherEvent);
}
}
}
@@ -304,7 +281,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;
}
@@ -320,16 +297,31 @@
};
mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
dismissTaskMenuView(mTarget);
- mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
- mTaskView.buildProto());
+ mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+ .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
}
}
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 226c818..37314ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -24,7 +24,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
-import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -540,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 */)));
@@ -636,9 +637,7 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1");
}
- if (!isFixedRotationTransformEnabled()) {
- return;
- }
+
baseInputConsumer.notifyOrientationSetup();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 14215a1..a9f138e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -267,10 +267,6 @@
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
- // Until passing slop, we don't know what direction we're going, so assume we might
- // be quick switching to avoid translating recents away when continuing the gesture.
- boolean isLikelyToStartNewTask = !mPassedSlopOnThisGesture
- || horizontalDist > upDist;
if (!mPassedPilferInputSlop) {
if (passedSlop) {
@@ -304,6 +300,13 @@
}
if (mDeviceState.isFullyGesturalNavMode()) {
+ // Until passing slop, we don't know what direction we're going, so assume
+ // we're quick switching to avoid translating recents away when continuing
+ // the gesture.
+ boolean haveNotPassedSlopOnContinuedGesture =
+ !mPassedSlopOnThisGesture && mPassedPilferInputSlop;
+ boolean isLikelyToStartNewTask = haveNotPassedSlopOnContinuedGesture
+ || horizontalDist > upDist;
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 11fee2f..32b1c58 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -36,8 +36,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
-import java.util.function.Predicate;
-
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
@@ -50,8 +48,6 @@
private final InputMonitorCompat mInputMonitor;
private final int[] mLocationOnScreen = new int[2];
- private final boolean mProxyTouch;
- private final Predicate<MotionEvent> mEventReceiver;
private final boolean mStartingInActivityBounds;
private boolean mTargetHandledTouch;
@@ -64,15 +60,7 @@
mActivityInterface = gestureState.getActivityInterface();
mTarget = activity.getDragLayer();
- if (startingInActivityBounds) {
- mEventReceiver = mTarget::dispatchTouchEvent;
- mProxyTouch = true;
- } else {
- // Only proxy touches to controllers if we are starting touch from nav bar.
- mEventReceiver = mTarget::proxyTouchEvent;
- mTarget.getLocationOnScreen(mLocationOnScreen);
- mProxyTouch = mTarget.prepareProxyEventStarting();
- }
+ mTarget.getLocationOnScreen(mLocationOnScreen);
}
@Override
@@ -87,10 +75,6 @@
@Override
public void onMotionEvent(MotionEvent ev) {
- if (!mProxyTouch) {
- return;
- }
-
int flags = ev.getEdgeFlags();
if (!mStartingInActivityBounds) {
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
@@ -99,7 +83,7 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
}
- boolean handled = mEventReceiver.test(ev);
+ boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index e7b965b..3cafd42 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
@@ -41,6 +42,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.statehandlers.DepthController;
@@ -67,7 +69,7 @@
private final AnimatorSet mAnimators = new AnimatorSet();
public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim) {
- prepareToAnimate(launcher);
+ prepareToAnimate(launcher, animateOverviewScrim);
mVelocity = velocity;
@@ -129,8 +131,9 @@
}
if (animateOverviewScrim) {
- addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
- addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+ PendingAnimation pendingAnimation = new PendingAnimation(ALPHA_DURATION_MS);
+ addScrimAnimationForState(launcher, NORMAL, pendingAnimation);
+ mAnimators.play(pendingAnimation.buildAnim());
}
addDepthAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
@@ -153,7 +156,7 @@
/**
* Setup workspace with 0 duration to prepare for our staggered animation.
*/
- private void prepareToAnimate(Launcher launcher) {
+ private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
StateAnimationConfig config = new StateAnimationConfig();
config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
config.duration = 0;
@@ -162,12 +165,21 @@
// Stop scrolling so that it doesn't interfere with the translation offscreen.
launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
+
+ if (animateOverviewScrim) {
+ addScrimAnimationForState(launcher, BACKGROUND_APP, NO_ANIM_PROPERTY_SETTER);
+ }
}
public AnimatorSet getAnimators() {
return mAnimators;
}
+ public StaggeredWorkspaceAnim addAnimatorListener(Animator.AnimatorListener listener) {
+ mAnimators.addListener(listener);
+ return this;
+ }
+
/**
* Starts the animation.
*/
@@ -224,15 +236,14 @@
mAnimators.play(alpha);
}
- private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
- PendingAnimation builder = new PendingAnimation(duration);
- launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
- builder.setFloat(
+ private void addScrimAnimationForState(Launcher launcher, LauncherState state,
+ PropertySetter setter) {
+ launcher.getWorkspace().getStateTransitionAnimation().setScrim(setter, state);
+ setter.setFloat(
launcher.getDragLayer().getOverviewScrim(),
OverviewScrim.SCRIM_PROGRESS,
state.getOverviewScrimAlpha(launcher),
ACCEL_DEACCEL);
- mAnimators.play(builder.buildAnim());
}
private void addDepthAnimationForState(Launcher launcher, LauncherState state, long duration) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 196a7c4..32da52e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -141,7 +141,8 @@
*/
public void setPreview(RemoteAnimationTargetCompat runningTarget) {
setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
- mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y);
+ mRunningTargetWindowPosition.set(runningTarget.screenSpaceBounds.left,
+ runningTarget.screenSpaceBounds.top);
}
/**
@@ -255,8 +256,8 @@
float taskHeight = mTaskRect.height();
mMatrix.set(mPositionHelper.getMatrix());
- mMatrix.postScale(scale, scale);
mMatrix.postTranslate(insets.left, insets.top);
+ mMatrix.postScale(scale, scale);
// Apply TaskView matrix: translate, scale, scroll
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
index 6b99f90..0979c07 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
@@ -232,7 +232,7 @@
launcher, parent);
view.init(launcher);
launcher.getDragLayer().addView(view);
- launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
+ launcher.getStatsLogManager().logger().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
view.requestLayout();
view.playAnimation();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 95eb10f..a2da398 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -24,7 +24,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.FrameLayout;
@@ -39,6 +38,7 @@
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
+import com.android.quickstep.util.LayoutUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -68,6 +68,15 @@
public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
public static final int HIDDEN_NO_RECENTS = 1 << 5;
+ @IntDef(flag = true, value = {
+ DISABLED_SCROLLING,
+ DISABLED_ROTATED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActionsDisabledFlags { }
+
+ public static final int DISABLED_SCROLLING = 1 << 0;
+ public static final int DISABLED_ROTATED = 1 << 1;
+
private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_VISIBILITY_ALPHA = 1;
private static final int INDEX_FULLSCREEN_ALPHA = 2;
@@ -78,6 +87,9 @@
@ActionsHiddenFlags
private int mHiddenFlags;
+ @ActionsDisabledFlags
+ protected int mDisabledFlags;
+
protected T mCallbacks;
public OverviewActionsView(Context context) {
@@ -117,7 +129,6 @@
@Override
public void onClick(View view) {
if (mCallbacks == null) {
- Log.d("OverviewActionsView", "Callbacks null onClick");
return;
}
int id = view.getId();
@@ -158,6 +169,25 @@
setVisibility(isHidden ? INVISIBLE : VISIBLE);
}
+ /**
+ * Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled.
+ * Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable
+ * buttons individually, currently done for select button in subclass.
+ *
+ * @param disabledFlags The flag to update.
+ * @param enable Whether to enable the disable flag: True will cause view to be disabled.
+ */
+ public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) {
+ if (enable) {
+ mDisabledFlags |= disabledFlags;
+ } else {
+ mDisabledFlags &= ~disabledFlags;
+ }
+ //
+ boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
+ LayoutUtils.setViewEnabled(this, isEnabled);
+ }
+
public AlphaProperty getContentAlpha() {
return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
}
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 64d90cf..6380bbe 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
@@ -30,6 +30,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -207,7 +208,7 @@
}
};
- protected final RecentsOrientedState mOrientationState;
+ protected RecentsOrientedState mOrientationState;
protected final BaseActivityInterface mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -370,11 +371,17 @@
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
- if (!inMultiWindowMode && mOverviewStateEnabled) {
- // TODO: Re-enable layout transitions for addition of the unpinned task
- reloadIfNeeded();
- }
- };
+ if (mOrientationState != null) {
+ mOrientationState.setMultiWindowMode(inMultiWindowMode);
+ setLayoutRotation(mOrientationState.getTouchRotation(),
+ mOrientationState.getDisplayRotation());
+ rotateAllChildTasks();
+ }
+ if (!inMultiWindowMode && mOverviewStateEnabled) {
+ // TODO: Re-enable layout transitions for addition of the unpinned task
+ reloadIfNeeded();
+ }
+ };
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
BaseActivityInterface sizeStrategy) {
@@ -487,7 +494,8 @@
return;
}
mModel.getIconCache().clear();
- reset();
+ unloadVisibleTaskData();
+ loadVisibleTaskData();
}
public void init(OverviewActionsView actionsView) {
@@ -607,17 +615,28 @@
}
}
+ /**
+ * 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
protected void onPageBeginTransition() {
super.onPageBeginTransition();
- LayoutUtils.setViewEnabled(mActionsView, false);
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
- if (getScrollX() == getScrollForPage(getPageNearestToCenterOfScreen())) {
- LayoutUtils.setViewEnabled(mActionsView, true);
+ if (isClearAllHidden()) {
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
@@ -779,8 +798,10 @@
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView taskView = getTaskViewAt(i);
if (mIgnoreResetTaskId != taskView.getTask().key.id) {
- taskView.resetVisualProperties();
+ taskView.resetViewTransforms();
taskView.setStableAlpha(mContentAlpha);
+ taskView.setFullscreenProgress(mFullscreenProgress);
+ taskView.setModalness(mTaskModalness);
}
}
if (mRunningTaskTileHidden) {
@@ -828,7 +849,6 @@
private void resetPaddingFromTaskSize() {
DeviceProfile dp = mActivity.getDeviceProfile();
- mOrientationState.setMultiWindowMode(dp.isMultiWindowMode);
getTaskSize(mTempRect);
mTaskWidth = mTempRect.width();
mTaskHeight = mTempRect.height();
@@ -980,6 +1000,7 @@
mDwbToastShown = false;
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
LayoutUtils.setViewEnabled(mActionsView, true);
+ mOrientationState.setGestureActive(false);
}
public @Nullable TaskView getRunningTaskView() {
@@ -1017,6 +1038,7 @@
*/
public void onGestureAnimationStart(int runningTaskId) {
// This needs to be called before the other states are set since it can create the task view
+ mOrientationState.setGestureActive(true);
showCurrentTask(runningTaskId);
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
@@ -1041,8 +1063,7 @@
private void animateRecentsRotationInPlace(int newRotation) {
if (mOrientationState.canLauncherRotate()) {
- // Update the rotation but let system take care of the rotation animation
- setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
+ // Let system take care of the rotation
return;
}
AnimatorSet pa = setRecentsChangedOrientation(true);
@@ -1080,6 +1101,8 @@
* Called when a gesture from an app has finished.
*/
public void onGestureAnimationEnd() {
+ mOrientationState.setGestureActive(false);
+
setOnScrollChangeListener(null);
setEnableFreeScroll(true);
setEnableDrawingLiveTile(true);
@@ -1335,8 +1358,8 @@
ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.UP, index, compKey);
- mActivity.getStatsLogManager().log(
- LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
+ mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
+ .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
}
}
@@ -1627,7 +1650,7 @@
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
- boolean isInLandscape = touchRotation != 0
+ boolean isInLandscape = mOrientationState.getTouchRotation() != 0
|| mOrientationState.getLauncherRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
!mOrientationState.canLauncherRotate() && isInLandscape);
@@ -1716,11 +1739,13 @@
setPivotY(mTempPointF.y);
setTaskModalness(mTaskModalness);
updatePageOffsets();
+ setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
private void updatePageOffsets() {
float offset = mAdjacentPageOffset * getWidth();
- float modalOffset = mTaskModalness * getWidth();
+ float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth();
if (mIsRtl) {
offset = -offset;
modalOffset = -modalOffset;
@@ -1748,6 +1773,16 @@
return Math.max(getWidth(), 1);
}
+ /**
+ * Resets the visuals when exit modal state.
+ */
+ public void resetModalVisuals() {
+ TaskView taskView = getCurrentPageTaskView();
+ if (taskView != null) {
+ taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
+ }
+ }
+
private void updateDeadZoneRects() {
// Get the deadzone rect surrounding the clear all button to not dismiss overview to home
mClearAllButtonDeadZoneRect.setEmpty();
@@ -1900,7 +1935,7 @@
anim.play(depthAnimator);
}
anim.play(progressAnim);
- anim.setDuration(duration).setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
@@ -1918,8 +1953,8 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
- mActivity.getStatsLogManager().log(
- LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
+ mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
+ .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
}
} else {
onTaskLaunched(false);
@@ -2089,6 +2124,12 @@
return mClearAllButton;
}
+ @Override
+ protected boolean onOverscroll(int amount) {
+ // overscroll should only be accepted on -1 direction (for clear all button)
+ if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
+ return super.onOverscroll(amount);
+ }
/**
* @return How many pixels the running task is offset on the currently laid out dominant axis.
@@ -2182,6 +2223,10 @@
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);
}
@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 ead0c8b..b2f937f 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
@@ -27,6 +27,7 @@
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
@@ -34,12 +35,15 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.Surface;
import android.view.View;
+import androidx.annotation.RequiresApi;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -211,6 +215,38 @@
return fallback;
}
+ /**
+ * Get the scaled insets that are being used to draw the task view. This is a subsection of
+ * the full snapshot.
+ * @return the insets in snapshot bitmap coordinates.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.Q)
+ public Insets getScaledInsets() {
+ if (mThumbnailData == null) {
+ return Insets.NONE;
+ }
+
+ RectF bitmapRect = new RectF(
+ 0, 0,
+ mThumbnailData.thumbnail.getWidth(), mThumbnailData.thumbnail.getHeight());
+ RectF viewRect = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
+
+ // The position helper matrix tells us how to transform the bitmap to fit the view, the
+ // inverse tells us where the view would be in the bitmaps coordinates. The insets are the
+ // difference between the bitmap bounds and the projected view bounds.
+ Matrix boundsToBitmapSpace = new Matrix();
+ mPreviewPositionHelper.getMatrix().invert(boundsToBitmapSpace);
+ RectF boundsInBitmapSpace = new RectF();
+ boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
+
+ return Insets.of(
+ Math.round(boundsInBitmapSpace.left),
+ Math.round(boundsInBitmapSpace.top),
+ Math.round(bitmapRect.right - boundsInBitmapSpace.right),
+ Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom));
+ }
+
+
public int getSysUiStatusNavFlags() {
if (mThumbnailData != null) {
int flags = 0;
@@ -321,10 +357,9 @@
}
private void updateOverlay() {
- // The overlay doesn't really work when the screenshot is rotated, so don't add it.
- if (mOverlayEnabled && !mPreviewPositionHelper.mIsOrientationChanged
- && mBitmapShader != null && mThumbnailData != null) {
- mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix);
+ if (mOverlayEnabled && mBitmapShader != null && mThumbnailData != null) {
+ mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
+ mPreviewPositionHelper.mIsOrientationChanged);
} else {
mOverlay.reset();
}
@@ -412,6 +447,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 cadf6c4..8bdf99e 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,8 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
- mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
+ mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
+ .log(LAUNCHER_TASK_LAUNCH_TAP);
});
mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -226,14 +228,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,13 +429,17 @@
}
private boolean showTaskMenu(int action) {
- getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
- mMenuView = TaskMenuView.showForTask(this);
- mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
- UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
- LauncherLogProto.ItemType.TASK_ICON);
- if (mMenuView != null) {
- mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
+ if (!getRecentsView().isClearAllHidden()) {
+ getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
+ } else {
+ mMenuView = TaskMenuView.showForTask(this);
+ mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
+ .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
+ UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
+ LauncherLogProto.ItemType.TASK_ICON);
+ if (mMenuView != null) {
+ mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
+ }
}
return mMenuView != null;
}
@@ -537,7 +545,7 @@
setIconAndDimTransitionProgress(iconScale, invert);
}
- private void resetViewTransforms() {
+ protected void resetViewTransforms() {
setCurveScale(1);
setTranslationX(0f);
setTranslationY(0f);
@@ -546,12 +554,6 @@
setIconScaleAndDim(1);
}
- public void resetVisualProperties() {
- resetViewTransforms();
- setFullscreenProgress(0);
- setModalness(0);
- }
-
public void setStableAlpha(float parentAlpha) {
mStableAlpha = parentAlpha;
setAlpha(mStableAlpha);
@@ -969,6 +971,9 @@
}
void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
+ if (getRecentsView() == null) {
+ return;
+ }
mCurrentFullscreenParams.setProgress(
mFullscreenProgress,
getRecentsView().getScaleX(),
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 2b11ca0..0f2955b 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -17,7 +17,9 @@
<string name="task_overlay_factory_class" translatable="false"/>
<!-- Activities which block home gesture -->
- <string-array name="gesture_blocking_activities" translatable="false"/>
+ <string-array name="gesture_blocking_activities" translatable="false">
+ <item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
+ </string-array>
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a27c127..769d298 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 predictions 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/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
new file mode 100644
index 0000000..a31ba21
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.quickstep.util;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.shadows.LShadowDisplay;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.LauncherActivityInterface;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowDisplayManager;
+
+@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
+public class TaskViewSimulatorTest {
+
+ @Test
+ public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(1200, 2450)
+ .withInsets(new Rect(0, 80, 0, 120))
+ .verifyNoTransforms();
+ }
+
+ @Test
+ public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(1200, 2450)
+ .withInsets(new Rect(55, 80, 55, 120))
+ .verifyNoTransforms();
+ }
+
+ @Test
+ public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(2450, 1250)
+ .withInsets(new Rect(0, 80, 0, 40))
+ .verifyNoTransforms();
+ }
+
+ @Test
+ public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(2450, 1250)
+ .withInsets(new Rect(0, 80, 120, 0))
+ .verifyNoTransforms();
+ }
+
+ @Test
+ public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(2450, 1250)
+ .withInsets(new Rect(55, 80, 55, 120))
+ .verifyNoTransforms();
+ }
+
+ @Test
+ public void taskProperlyScaled_landscape_rotated() {
+ new TaskMatrixVerifier()
+ .withLauncherSize(1200, 2450)
+ .withInsets(new Rect(0, 80, 0, 120))
+ .withAppBounds(
+ new Rect(0, 0, 2450, 1200),
+ new Rect(0, 80, 0, 120),
+ Surface.ROTATION_90)
+ .verifyNoTransforms();
+ }
+
+ private static class TaskMatrixVerifier extends TransformParams {
+
+ private final Context mContext = RuntimeEnvironment.application;
+
+ private Rect mAppBounds = new Rect();
+ private Rect mLauncherInsets = new Rect();
+
+ private Rect mAppInsets;
+
+ private int mAppRotation = -1;
+ private DeviceProfile mDeviceProfile;
+
+ TaskMatrixVerifier withLauncherSize(int width, int height) {
+ ShadowDisplayManager.changeDisplay(DEFAULT_DISPLAY,
+ String.format("w%sdp-h%sdp-mdpi", width, height));
+ if (mAppBounds.isEmpty()) {
+ mAppBounds.set(0, 0, width, height);
+ }
+ return this;
+ }
+
+ TaskMatrixVerifier withInsets(Rect insets) {
+ LShadowDisplay shadowDisplay = Shadow.extract(
+ mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
+ shadowDisplay.setInsets(insets);
+ mLauncherInsets.set(insets);
+ return this;
+ }
+
+ TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
+ mAppBounds.set(bounds);
+ mAppInsets = insets;
+ mAppRotation = appRotation;
+ return this;
+ }
+
+ void verifyNoTransforms() {
+ mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext)
+ .getDeviceProfile(mContext);
+ mDeviceProfile.updateInsets(mLauncherInsets);
+
+ TaskViewSimulator tvs = new TaskViewSimulator(mContext,
+ LauncherActivityInterface.INSTANCE);
+ tvs.setDp(mDeviceProfile);
+
+ int launcherRotation = DefaultDisplay.INSTANCE.get(mContext).getInfo().rotation;
+ if (mAppRotation < 0) {
+ mAppRotation = launcherRotation;
+ }
+ tvs.setLayoutRotation(launcherRotation, mAppRotation);
+ if (mAppInsets == null) {
+ mAppInsets = new Rect(mLauncherInsets);
+ }
+ tvs.setPreviewBounds(mAppBounds, mAppInsets);
+
+ tvs.fullScreenProgress.value = 1;
+ tvs.recentsViewScale.value = tvs.getFullScreenScale();
+ tvs.apply(this);
+ }
+
+ @Override
+ public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
+ proxy.onBuildTargetParams(builder, null, this);
+ return new SurfaceParams[] {builder.build()};
+ }
+
+ @Override
+ public void applySurfaceParams(SurfaceParams[] params) {
+ // Verify that the task position remains the same
+ RectF newAppBounds = new RectF(mAppBounds);
+ params[0].matrix.mapRect(newAppBounds);
+ Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
+
+ System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
+ }
+ }
+
+ private static class AlmostSame extends TypeSafeMatcher<RectF> {
+
+ // Allow 1px error margin to account for float to int conversions
+ private final float mError = 1f;
+ private final Rect mExpected;
+
+ AlmostSame(Rect expected) {
+ mExpected = expected;
+ }
+
+ @Override
+ protected boolean matchesSafely(RectF item) {
+ return Math.abs(item.left - mExpected.left) < mError
+ && Math.abs(item.top - mExpected.top) < mError
+ && Math.abs(item.right - mExpected.right) < mError
+ && Math.abs(item.bottom - mExpected.bottom) < mError;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendValue(mExpected);
+ }
+ }
+}
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/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index fc60434..5ad0bca 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.os.Build;
import android.os.Handler;
-import android.util.Log;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
@@ -154,16 +153,8 @@
// Because t=0 has the app icon in its original spot, we can skip the
// first frame and have the same movement one frame earlier.
- int singleFrameMs = getSingleFrameMs(context);
- long playTime = singleFrameMs;
- // b/153821199 Add logs to debug crash but ensure release builds do not crash.
- if (Utilities.IS_DEBUG_DEVICE) {
- Log.e(TAG, "Total duration=[" + mAnimator.getTotalDuration()
- + "], singleFrameMs=[" + singleFrameMs + "], mAnimator=" + mAnimator);
- } else {
- playTime = Math.min(singleFrameMs, mAnimator.getTotalDuration());
- }
- mAnimator.setCurrentPlayTime(playTime);
+ mAnimator.setCurrentPlayTime(
+ Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
}
}
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index ebe9e26..2b08dcd 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -32,6 +32,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.TransactionCompat;
@@ -110,6 +111,10 @@
}
private void ensureDependencies() {
+ if (mWallpaperManager == null) {
+ mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
+ mWallpaperManager = new WallpaperManagerCompat(mLauncher);
+ }
if (mLauncher.getRootView() != null && mOnAttachListener == null) {
mOnAttachListener = new View.OnAttachStateChangeListener() {
@Override
@@ -127,11 +132,6 @@
};
mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
}
- if (mWallpaperManager != null) {
- return;
- }
- mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
- mWallpaperManager = new WallpaperManagerCompat(mLauncher);
}
/**
@@ -205,7 +205,8 @@
return;
}
- if (mSurface == null || !mSurface.isValid()) {
+ boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
+ if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
return;
}
mDepth = depthF;
@@ -214,17 +215,20 @@
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
}
- final int blur;
- if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
- // All apps has a solid background. We don't need to draw blurs after it's fully
- // visible. This will take us out of GPU composition, saving battery and increasing
- // performance.
- blur = 0;
- } else {
- blur = (int) (mDepth * mMaxBlurRadius);
+
+ if (supportsBlur) {
+ final int blur;
+ if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
+ // All apps has a solid background. We don't need to draw blurs after it's fully
+ // visible. This will take us out of GPU composition, saving battery and increasing
+ // performance.
+ blur = 0;
+ } else {
+ blur = (int) (mDepth * mMaxBlurRadius);
+ }
+ new TransactionCompat()
+ .setBackgroundBlurRadius(mSurface, blur)
+ .apply();
}
- new TransactionCompat()
- .setBackgroundBlurRadius(mSurface, blur)
- .apply();
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 8745814..20ee61d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -16,6 +16,8 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -36,7 +38,6 @@
import android.view.MotionEvent;
import android.view.animation.Interpolator;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -124,7 +125,7 @@
return false;
}
}
- if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) {
+ if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
return false;
}
return true;
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 adf2321..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;
@@ -23,7 +24,6 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
-import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
@@ -49,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;
@@ -113,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;
@@ -194,13 +241,29 @@
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() {
- if (!isFixedRotationTransformEnabled()) {
- return;
- }
-
ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener);
@@ -260,7 +323,7 @@
@Override
public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
- if (info.id != getDisplayId() || (flags & CHANGE_FRAME_DELAY) == CHANGE_FRAME_DELAY) {
+ if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) {
// ignore displays that aren't running launcher and frame refresh rate changes
return;
}
@@ -273,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);
+ }
}
/**
@@ -558,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) {
@@ -586,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) {
@@ -599,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
@@ -608,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 04f1169..e496807 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -17,6 +17,8 @@
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.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
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;
@@ -29,10 +31,11 @@
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
@@ -40,11 +43,14 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LogConfig;
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
+import java.util.Optional;
+import java.util.OptionalInt;
/**
* This class calls StatsLog compile time generated methods.
@@ -62,112 +68,31 @@
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) {
sContext = context;
}
- /**
- * Logs a {@link EventEnum}.
- */
@Override
- public void log(EventEnum event) {
- log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
+ public StatsLogger logger() {
+ return new StatsCompatLogger();
}
/**
- * Logs an event and accompanying {@link InstanceId}.
+ * Logs a ranking event and accompanying {@link InstanceId} and package name.
*/
@Override
- public void log(EventEnum event, InstanceId instanceId) {
- log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
- }
-
- /**
- * Logs an event and accompanying {@link ItemInfo}.
- */
- @Override
- public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) {
- log(event, DEFAULT_INSTANCE_ID, info);
- }
-
- /**
- * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
- */
- @Override
- public void log(EventEnum event, InstanceId instanceId,
- @Nullable LauncherAtom.ItemInfo info) {
- logInternal(event, instanceId, info,
- LAUNCHER_UICHANGED__DST_STATE__HOME,
- LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
- }
-
- /**
- * 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.
- */
- @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);
- }
-
- /**
- * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
- */
- private void logInternal(EventEnum event, InstanceId instanceId,
- @Nullable LauncherAtom.ItemInfo info, int srcState, int dstState) {
- info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
-
- if (IS_VERBOSE) {
- String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() :
- event.getId() + "";
-
- 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));
- }
-
- if (!Utilities.ATLEAST_R) {
- return;
- }
-
- SysUiStatsLog.write(
- SysUiStatsLog.LAUNCHER_EVENT,
- SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
- srcState,
- dstState,
- null /* launcher extensions, deprecated */,
- false /* quickstep_enabled, deprecated */,
- event.getId() /* event_id */,
- info.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 */);
+ 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; */);
}
/**
@@ -180,41 +105,46 @@
}
private class SnapshotWorker extends BaseModelUpdateTask {
+ private final InstanceId mInstanceId;
+ SnapshotWorker() {
+ mInstanceId = new InstanceIdSequence(
+ 1 << 20 /*InstanceId.INSTANCE_ID_MAX*/).newInstanceId();
+ }
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
-
for (ItemInfo info : workspaceItems) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo);
+ writeSnapshot(atomInfo, mInstanceId);
}
for (FolderInfo fInfo : folders) {
- for (ItemInfo info : fInfo.contents) {
+ ArrayList<WorkspaceItemInfo> folderContents = (ArrayList) fInfo.contents.clone();
+ for (ItemInfo info : folderContents) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
- writeSnapshot(atomInfo);
+ writeSnapshot(atomInfo, mInstanceId);
}
}
for (ItemInfo info : appWidgets) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo);
+ writeSnapshot(atomInfo, mInstanceId);
}
}
}
- private static void writeSnapshot(LauncherAtom.ItemInfo info) {
+ private static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
if (IS_VERBOSE) {
- Log.d(TAG, "\nwriteSnapshot:" + info);
+ Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
if (!Utilities.ATLEAST_R) {
return;
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
- 0 /* event_id */,
+ LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
info.getItemCase().getNumber() /* target_id */,
- 0 /* instance_id */,
+ instanceId.getId() /* instance_id */,
0 /* uid */,
getPackageName(info) /* package_name */,
getComponentName(info) /* component_name */,
@@ -226,12 +156,158 @@
getPageId(info, true) /* page_id_parent */,
getHierarchy(info) /* hierarchy */,
info.getIsWork() /* is_work_profile */,
- 0 /* origin TODO */,
- 0 /* cardinality */,
+ info.getAttribute().getNumber() /* origin */,
+ getCardinality(info) /* cardinality */,
info.getWidget().getSpanX(),
info.getWidget().getSpanY());
}
+ /**
+ * Helps to construct and write statsd compatible log message.
+ */
+ private static class StatsCompatLogger implements StatsLogger {
+
+ private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+ private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
+ private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+ private OptionalInt mRank = OptionalInt.empty();
+ private Optional<ContainerInfo> mContainerInfo = Optional.empty();
+ private int mSrcState = LAUNCHER_UICHANGED__SRC_STATE__HOME;
+ private int mDstState = LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
+
+ @Override
+ public StatsLogger withItemInfo(ItemInfo itemInfo) {
+ if (mContainerInfo.isPresent()) {
+ throw new IllegalArgumentException(
+ "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
+ }
+ this.mItemInfo = itemInfo;
+ return this;
+ }
+
+ @Override
+ public StatsLogger withInstanceId(InstanceId instanceId) {
+ this.mInstanceId = instanceId;
+ return this;
+ }
+
+ @Override
+ public StatsLogger withRank(int rank) {
+ this.mRank = OptionalInt.of(rank);
+ return this;
+ }
+
+ @Override
+ public StatsLogger withSrcState(int srcState) {
+ this.mSrcState = srcState;
+ return this;
+ }
+
+ @Override
+ public StatsLogger withDstState(int dstState) {
+ this.mDstState = dstState;
+ return this;
+ }
+
+ @Override
+ public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
+ if (mItemInfo != DEFAULT_ITEM_INFO) {
+ throw new IllegalArgumentException(
+ "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
+ }
+ this.mContainerInfo = Optional.of(containerInfo);
+ return this;
+ }
+
+ @Override
+ public void log(EventEnum event) {
+ if (!Utilities.ATLEAST_R) {
+ return;
+ }
+
+ LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+ (LauncherAtom.ItemInfo.Builder) mItemInfo.buildProto().toBuilder();
+ mRank.ifPresent(itemInfoBuilder::setRank);
+ if (mContainerInfo.isPresent()) {
+ // User already provided container info;
+ // default container info from item info will be ignored.
+ itemInfoBuilder.setContainerInfo(mContainerInfo.get());
+ write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+ return;
+ }
+
+ if (mItemInfo.container < 0) {
+ // Item is not within a folder. Write to StatsLog in same thread.
+ write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+ } else {
+ // Item is inside the folder, fetch folder info in a BG thread
+ // and then write to StatsLog.
+ LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel,
+ AllAppsList apps) {
+ FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
+ LauncherAtom.ItemInfo.Builder atomInfoBuilder =
+ (LauncherAtom.ItemInfo.Builder) mItemInfo
+ .buildProto(folderInfo).toBuilder();
+ mRank.ifPresent(atomInfoBuilder::setRank);
+ write(event, mInstanceId, atomInfoBuilder.build(), mSrcState,
+ mDstState);
+ }
+ });
+ }
+ }
+
+ private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
+ int srcState, int dstState) {
+ 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", name, getStateString(srcState),
+ getStateString(dstState), atomInfo)
+ : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
+ getStateString(srcState), getStateString(dstState), instanceId,
+ atomInfo));
+ }
+
+ SysUiStatsLog.write(
+ SysUiStatsLog.LAUNCHER_EVENT,
+ SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
+ srcState,
+ dstState,
+ null /* launcher extensions, deprecated */,
+ false /* quickstep_enabled, deprecated */,
+ event.getId() /* event_id */,
+ atomInfo.getItemCase().getNumber() /* target_id */,
+ instanceId.getId() /* instance_id TODO */,
+ 0 /* uid TODO */,
+ 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 */);
+ }
+ }
+
+ 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:
@@ -308,7 +384,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/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 3f58e01..cefab1b 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -74,17 +74,14 @@
/**
* Recursively sets view and all children enabled/disabled.
- * @param viewGroup Top most parent view to change.
+ * @param view Top most parent view to change.
* @param enabled True = enable, False = disable.
*/
- public static void setViewEnabled(ViewGroup viewGroup, boolean enabled) {
- viewGroup.setEnabled(enabled);
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- View child = viewGroup.getChildAt(i);
- if (child instanceof ViewGroup) {
- setViewEnabled((ViewGroup) child, enabled);
- } else {
- child.setEnabled(enabled);
+ public static void setViewEnabled(View view, boolean enabled) {
+ view.setEnabled(enabled);
+ if (view instanceof ViewGroup) {
+ for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
+ setViewEnabled(((ViewGroup) view).getChildAt(i), enabled);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 7e8222c..0fdc684 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.showEdu();
+ stateManager.removeStateListener(this);
+ }
+ }
+ }
+ });
+ }
+
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
&& FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
stateManager.addStateListener(new StateListener<LauncherState>() {
@@ -121,7 +144,7 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL) {
- if (mCount == MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
+ if (mCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
AllAppsEduView.show(launcher);
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 922f5ac..90ee18f 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;
@@ -72,8 +73,6 @@
private static final String TAG = "RecentsOrientedState";
private static final boolean DEBUG = true;
- private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
-
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -94,43 +93,37 @@
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
// Multiple orientation is only supported if density is < 600
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY = 1 << 1;
- // Feature flag controlling the multi-orientation feature
- private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG = 1 << 2;
// Shared prefs for rotation, only if activity supports it
- private static final int FLAG_HOME_ROTATION_ALLOWED_IN_PREFS = 1 << 3;
+ private static final int FLAG_HOME_ROTATION_ALLOWED_IN_PREFS = 1 << 2;
// If the user has enabled system rotation
- private static final int FLAG_SYSTEM_ROTATION_ALLOWED = 1 << 4;
+ private static final int FLAG_SYSTEM_ROTATION_ALLOWED = 1 << 3;
// Multiple orientation is not supported in multiwindow mode
- private static final int FLAG_MULTIWINDOW_ROTATION_ALLOWED = 1 << 5;
+ private static final int FLAG_MULTIWINDOW_ROTATION_ALLOWED = 1 << 4;
// Whether to rotation sensor is supported on the device
- private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 6;
+ private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 5;
// Whether to enable rotation watcher when multi-rotation is supported
- private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 7;
+ private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
// Enable home rotation for UI tests, ignoring home rotation value from prefs
- private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 8;
+ private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 7;
+ // Whether the swipe gesture is running, so the recents would stay locked in the
+ // current orientation
+ private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
- | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY
- | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
+ | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
// State for which rotation watcher will be enabled. We skip it when home rotation or
// multi-window is enabled as in that case, activity itself rotates.
private static final int VALUE_ROTATION_WATCHER_ENABLED =
MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
- | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
-
- private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
- newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
-
- /** TODO: Remove once R ships. This is unlikely to change across different swipe gestures. */
- private static boolean sFixedRotationEnabled;
+ | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED
+ | FLAG_SWIPE_UP_NOT_RUNNING;
private final Context mContext;
private final ContentResolver mContentResolver;
private final SharedPreferences mSharedPrefs;
private final OrientationEventListener mOrientationListener;
- private final BaseActivityInterface mSizeStrategy;
private final Matrix mTmpMatrix = new Matrix();
@@ -147,7 +140,6 @@
mContext = context;
mContentResolver = context.getContentResolver();
mSharedPrefs = Utilities.getPrefs(context);
- mSizeStrategy = sizeStrategy;
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int degrees) {
@@ -168,11 +160,7 @@
if (originalSmallestWidth < 600) {
mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
}
- sFixedRotationEnabled = Settings.Global.getInt(
- context.getContentResolver(), FIXED_ROTATION_TRANSFORM_SETTING_NAME, 1) == 1;
- if (sFixedRotationEnabled) {
- mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
- }
+ mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
initFlags();
}
@@ -184,6 +172,13 @@
}
/**
+ * Sets if the swipe up gesture is currently running or not
+ */
+ public void setGestureActive(boolean isGestureActive) {
+ setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
+ }
+
+ /**
* Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
* @param touchRotation The rotation the nav bar region that is touched is in
* @param displayRotation Rotation of the display/device
@@ -210,7 +205,7 @@
mPreviousRotation = touchRotation;
if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
- mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
+ mOrientationHandler = PagedOrientationHandler.PORTRAIT;
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
@@ -280,10 +275,9 @@
private void initFlags() {
SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
- if (mOrientationListener.canDetectOrientation() &&
- currentMode != TWO_BUTTONS) {
- mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
- }
+ boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
+ currentMode != TWO_BUTTONS;
+ setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
// initialize external flags
updateAutoRotateSetting();
@@ -300,9 +294,6 @@
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
false, mSystemAutoRotateObserver);
- SysUINavigationMode.Mode currentMode =
- SysUINavigationMode.INSTANCE.get(mContext)
- .addModeChangeListener(mNavModeChangeListener);
}
initFlags();
}
@@ -314,8 +305,6 @@
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
- SysUINavigationMode.INSTANCE.get(mContext)
- .removeModeChangeListener(mNavModeChangeListener);
}
setRotationWatcherEnabled(false);
}
@@ -361,20 +350,6 @@
setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled);
}
- public int getTouchRotationDegrees() {
- switch (mTouchRotation) {
- case ROTATION_90:
- return 90;
- case ROTATION_180:
- return 180;
- case ROTATION_270:
- return 270;
- case ROTATION_0:
- default:
- return 0;
- }
- }
-
/**
* Returns the scale and pivot so that the provided taskRect can fit the provided full size
*/
@@ -458,7 +433,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) {
@@ -481,7 +457,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
@@ -521,14 +498,6 @@
}
}
- /**
- * Returns true if system can keep Launcher fixed to portrait layout even if the
- * foreground app is rotated
- */
- public static boolean isFixedRotationTransformEnabled() {
- return sFixedRotationEnabled;
- }
-
@NonNull
@Override
public String toString() {
@@ -545,4 +514,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/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index f5498c9..19e278b 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
import android.content.Context;
import android.graphics.Canvas;
@@ -187,6 +188,7 @@
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
+ updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
@@ -241,6 +243,22 @@
}
@Override
+ protected void updateSysUiColors() {
+ if (mDrawingFlatColor) {
+ super.updateSysUiColors();
+ } else {
+ // Use a light system UI (dark icons) if all apps is behind at least half of the
+ // status bar.
+ boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
+ if (forceChange) {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+ } else {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+ }
+ }
+ }
+
+ @Override
protected boolean shouldDragHandleBeVisible() {
boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
&& !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index a726052..bd8ab08 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -37,7 +37,6 @@
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -58,10 +57,8 @@
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureRewriterRule;
import com.android.launcher3.util.rule.FailureWatcher;
-import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -95,7 +92,8 @@
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
mLauncher = new LauncherInstrumentation();
- mLauncher.enableCheckEventsForSuccessfulGestures();
+ // b/143488140
+ //mLauncher.enableCheckEventsForSuccessfulGestures();
if (TestHelpers.isInLauncherProcess()) {
Utilities.enableRunningInTestHarnessForTests();
@@ -122,6 +120,9 @@
TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
getLauncherCommand(getLauncherInMyProcess()));
+ // b/143488140
+ mDevice.pressHome();
+ mDevice.waitForIdle();
}
}
};
@@ -132,9 +133,9 @@
}
}
- @NavigationModeSwitch
+ // b/143488140
+ //@NavigationModeSwitch
@Test
- @Ignore // b/143488140
public void goToOverviewFromHome() {
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
@@ -143,9 +144,9 @@
mLauncher.getBackground().switchToOverview();
}
- @NavigationModeSwitch
+ // b/143488140
+ //@NavigationModeSwitch
@Test
- @Ignore // b/143488140
public void goToOverviewFromApp() {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@@ -178,9 +179,9 @@
return mLauncher.getBackground().switchToOverview();
}
- @NavigationModeSwitch
+ // b/143488140
+ //@NavigationModeSwitch
@Test
- @Ignore // b/143488140
public void testOverview() {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 3d048a6..0c5b9ad 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -25,7 +25,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
-import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -46,6 +45,8 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ // b/143488140
+ mLauncher.pressHome();
// Start an activity where the gestures start.
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
}
@@ -99,5 +100,6 @@
// The test action.
mLauncher.getBackground().switchToOverview();
}
+ mLauncher.pressHome();
}
}
\ No newline at end of file
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/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 08e1c98..7f1107f 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -15,6 +15,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="?attr/allAppsScrimColor"
android:padding="48dp"
android:orientation="vertical"
android:gravity="center">
@@ -34,6 +35,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:id="@+id/work_apps_paused_content"
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
index 9cb7ce8..b5237db 100644
--- a/res/layout/work_mode_switch.xml
+++ b/res/layout/work_mode_switch.xml
@@ -28,11 +28,11 @@
android:gravity="start"
android:lines="1"
android:showText="false"
- android:textSize="16sp"
+ android:textSize="@dimen/work_profile_footer_text_size"
android:background="?attr/allAppsScrimColor"
android:text="@string/work_profile_toggle_label"
- android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding"
+ android:paddingBottom="@dimen/work_profile_footer_padding"
+ android:paddingLeft="@dimen/work_profile_footer_padding"
+ android:paddingRight="@dimen/work_profile_footer_padding"
+ android:paddingTop="@dimen/work_profile_footer_padding"
/>
diff --git a/res/values/config.xml b/res/values/config.xml
index 4cbc597..ca25325 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -144,6 +144,10 @@
<item name="staggered_stiffness" type="dimen" format="float">150</item>
<dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
+ <item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
+ <item name="hint_scale_stiffness" type="dimen" format="float">200</item>
+ <dimen name="hint_scale_velocity_dp_per_s">0.3dp</dimen>
+
<!-- Swipe up to home related -->
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_y_overshoot">10dp</dimen>
@@ -175,6 +179,10 @@
<item>@dimen/swipe_up_fling_min_visible_change</item>
<item>@dimen/swipe_up_y_overshoot</item>
+
+ <item>@dimen/hint_scale_damping_ratio</item>
+ <item>@dimen/hint_scale_stiffness</item>
+ <item>@dimen/hint_scale_velocity_dp_per_s</item>
</array>
<string-array name="live_wallpapers_remove_sysui_scrims">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 31b12ad..947e635 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -96,7 +96,6 @@
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
- <dimen name="all_apps_work_profile_tab_footer_padding">20dp</dimen>
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
@@ -105,6 +104,9 @@
<dimen name="all_apps_divider_margin_vertical">8dp</dimen>
+ <dimen name="work_profile_footer_padding">20dp</dimen>
+ <dimen name="work_profile_footer_text_size">16sp</dimen>
+
<!-- Widget tray -->
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2efa66f..935bb40 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -172,8 +172,10 @@
<string name="folder_closed">Folder closed</string>
<!-- Folder renamed format -->
<string name="folder_renamed">Folder renamed to <xliff:g id="name" example="Games">%1$s</xliff:g></string>
- <!-- Folder name format -->
- <string name="folder_name_format">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Folder name format when folder has less than 4 items -->
+ <string name="folder_name_format_exact">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> items</string>
+ <!-- Folder name format when folder has 4 or more items shown in preview-->
+ <string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
<!-- Strings for the customization mode -->
<!-- Text for widget add button -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a922183..ac00488 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,7 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="folderTextColor">#FF212121</item>
- <item name="folderHintColor">#FF616161</item>
+ <item name="folderHintColor">#89616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
<item name="workProfileOverlayTextColor">#FF212121</item>
@@ -106,7 +106,7 @@
<item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">@android:color/white</item>
- <item name="folderHintColor">#FFCCCCCC</item>
+ <item name="folderHintColor">#89CCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
<item name="iconOnlyShortcutColor">#B3FFFFFF</item>
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index b171712..a8e0cb3 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -5,6 +5,7 @@
com.android.launcher3.shadows.LShadowAppWidgetManager \
com.android.launcher3.shadows.LShadowBackupManager \
com.android.launcher3.shadows.LShadowBitmap \
+ com.android.launcher3.shadows.LShadowDisplay \
com.android.launcher3.shadows.LShadowLauncherApps \
com.android.launcher3.shadows.LShadowTypeface \
com.android.launcher3.shadows.LShadowUserManager \
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 7ca416d..8f3a83e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -90,6 +90,22 @@
}
@Test
+ public void testCustomProfileLoaded_with_folder_custom_title() throws Exception {
+ writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder("CustomFolder")
+ .addApp(TEST_PACKAGE, TEST_PACKAGE)
+ .addApp(TEST_PACKAGE, TEST_PACKAGE)
+ .addApp(TEST_PACKAGE, TEST_PACKAGE)
+ .build());
+
+ // Verify folder
+ assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
+ ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
+ assertEquals(3, ((FolderInfo) info).contents.size());
+ assertEquals("CustomFolder", info.title.toString());
+ }
+
+ @Test
public void testCustomProfileLoaded_with_widget() throws Exception {
String pendingAppPkg = "com.test.pending";
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 2584f65..3a252dc 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -92,7 +92,8 @@
SCREEN, CELLX, CELLY, RESTORED, INTENT
});
- mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp);
+ mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp,
+ new UserManagerState());
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 4b0ae7e..87fe3c0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -199,7 +199,7 @@
}
@Override
- public void bindAllApplications(AppInfo[] apps) {
+ public void bindAllApplications(AppInfo[] apps, int flags) {
mAppInfos = apps;
}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
new file mode 100644
index 0000000..3813fa1
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
@@ -0,0 +1,54 @@
+/*
+ * 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.shadows;
+
+import static org.robolectric.shadow.api.Shadow.directlyOn;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.ShadowDisplay;
+
+/**
+ * Extension of {@link ShadowDisplay} with missing shadow methods
+ */
+@Implements(value = Display.class)
+public class LShadowDisplay extends ShadowDisplay {
+
+ private final Rect mInsets = new Rect();
+
+ @RealObject Display realObject;
+
+ /**
+ * Sets the insets for the display
+ */
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ }
+
+ @Override
+ protected void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
+ directlyOn(realObject, Display.class).getCurrentSizeRange(outSmallestSize, outLargestSize);
+ outSmallestSize.x -= mInsets.left + mInsets.right;
+ outLargestSize.x -= mInsets.left + mInsets.right;
+
+ outSmallestSize.y -= mInsets.top + mInsets.bottom;
+ outLargestSize.y -= mInsets.top + mInsets.bottom;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
index d3659eb..4e21dce 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
@@ -47,6 +47,7 @@
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
+ private static final String ATTR_TITLE_TEXT = "titleText";
private static final String ATTR_SCREEN = "screen";
// x and y can be specified as negative integers, in which case -1 represents the
@@ -145,8 +146,17 @@
}
public FolderBuilder putFolder(int titleResId) {
- FolderBuilder folderBuilder = new FolderBuilder();
items.put(ATTR_TITLE, Integer.toString(titleResId));
+ return putFolder();
+ }
+
+ public FolderBuilder putFolder(String title) {
+ items.put(ATTR_TITLE_TEXT, title);
+ return putFolder();
+ }
+
+ private FolderBuilder putFolder() {
+ FolderBuilder folderBuilder = new FolderBuilder();
items.put(ATTR_CHILDREN, folderBuilder.mChildren);
mNodes.add(Pair.create(TAG_FOLDER, items));
return folderBuilder;
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/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 5971a02..432073e 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -132,6 +132,7 @@
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
+ private static final String ATTR_TITLE_TEXT = "titleText";
private static final String ATTR_SCREEN = "screen";
// x and y can be specified as negative integers, in which case -1 represents the
@@ -585,7 +586,8 @@
if (titleResId != 0) {
title = mSourceRes.getString(titleResId);
} else {
- title = "";
+ String titleText = getAttributeValue(parser, ATTR_TITLE_TEXT);
+ title = TextUtils.isEmpty(titleText) ? "" : titleText;
}
mValues.put(Favorites.TITLE, title);
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 268b910..9cb8cf2 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -43,6 +43,8 @@
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -188,7 +190,8 @@
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) {
- getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item.buildProto());
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ logAppLaunch(item, instanceId);
}
return true;
} catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
@@ -198,6 +201,11 @@
return false;
}
+ protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+ getStatsLogManager().logger().withItemInfo(info).withInstanceId(instanceId)
+ .log(LAUNCHER_APP_LAUNCH_TAP);
+ }
+
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
@Nullable String sourceContainer) {
try {
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index d75d712..2857497 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -126,11 +126,9 @@
d.dragInfo.container = NO_ID;
}
super.onDrop(d, options);
- mStatsLogManager.log(
- mControlType == ControlType.REMOVE_TARGET
- ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
- : LAUNCHER_ITEM_DROPPED_ON_CANCEL,
- d.logInstanceId);
+ mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+ .log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+ : LAUNCHER_ITEM_DROPPED_ON_CANCEL);
}
@Override
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 60abc66..e39e89c 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -148,6 +148,7 @@
iconSize = p.iconSize;
iconShapePath = p.iconShapePath;
landscapeIconSize = p.landscapeIconSize;
+ iconBitmapSize = p.iconBitmapSize;
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1f84c42..ec32e62 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2455,8 +2455,9 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAllApplications(AppInfo[] apps) {
- mAppsView.getAppsStore().setApps(apps);
+ @Override
+ public void bindAllApplications(AppInfo[] apps, int flags) {
+ mAppsView.getAppsStore().setApps(apps, flags);
}
/**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 3b1c7bb..f434c91 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -117,7 +117,7 @@
@Override
public void run() {
if (mModelLoaded && hasShortcutsPermission(mApp.getContext())
- != mBgDataModel.hasShortcutHostPermission) {
+ != mBgAllAppsList.hasShortcutHostPermission()) {
forceReload();
}
}
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/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index fbac0bd..499b54f 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -220,9 +220,11 @@
d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
super.onDrop(d, options);
if (mCurrentAccessibilityAction == UNINSTALL) {
- mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
+ mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+ .log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
} else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
+ mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+ .log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
}
}
@@ -348,10 +350,12 @@
mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
mDragObject.dragSource = mOriginal;
mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
- mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
+ mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
+ .log(LAUNCHER_ITEM_UNINSTALL_COMPLETED);
} else {
sendFailure();
- mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
+ mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
+ .log(LAUNCHER_ITEM_UNINSTALL_CANCELLED);
}
}
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..fb58f21 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -415,14 +415,9 @@
// Always enter the spring loaded mode
mLauncher.getStateManager().goToState(SPRING_LOADED);
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
- dragObject.logInstanceId,
- dragObject.dragSource instanceof Folder
- ? dragObject.originalDragInfo
- .buildProto(((Folder) dragObject.dragSource).mInfo)
- : dragObject.originalDragInfo.buildProto()
- );
+ mStatsLogManager.logger().withItemInfo(dragObject.originalDragInfo)
+ .withInstanceId(dragObject.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
}
public void deferRemoveExtraEmptyScreen() {
@@ -1342,7 +1337,6 @@
ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
stepAnimator.addUpdateListener(listener);
- stepAnimator.setDuration(config.duration);
stepAnimator.addListener(listener);
animation.add(stepAnimator);
}
@@ -1650,10 +1644,8 @@
Rect folderLocation = new Rect();
float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
target.removeView(v);
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
- d.logInstanceId,
- destInfo.buildProto(null));
+ mStatsLogManager.logger().withItemInfo(destInfo).withInstanceId(d.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED);
FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
targetCell[1]);
destInfo.cellX = -1;
@@ -1691,10 +1683,8 @@
if (dropOverView instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) dropOverView;
if (fi.acceptDrop(d.dragInfo)) {
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- fi.mInfo.buildProto(null));
+ mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
// if the drag started here, we need to remove it from the workspace
@@ -1897,10 +1887,8 @@
mLauncher.getStateManager().goToState(
NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(null));
+ mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
}
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -2438,10 +2426,9 @@
// widgets/shortcuts/folders in a slightly different way
mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
item.spanX, item.spanY);
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(null));
+ mStatsLogManager.logger().withItemInfo(d.dragInfo)
+ .withInstanceId(d.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
}
};
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2530,10 +2517,8 @@
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
resetTransitionTransform();
}
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(null));
+ mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
}
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 06a73db..cd938e1 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
+
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
@@ -23,11 +25,13 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_HAS_BACKGROUNDS;
+import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
+import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
@@ -35,6 +39,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import android.animation.ValueAnimator;
import android.view.View;
import android.view.animation.Interpolator;
@@ -43,8 +48,11 @@
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DynamicResource;
+import com.android.systemui.plugins.ResourceProvider;
/**
* Manages the animations between each of the workspace states.
@@ -104,17 +112,32 @@
View qsbView = qsbScaleView.getSearchView();
if (playAtomicComponent) {
Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
- propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+ LauncherState fromState = mLauncher.getStateManager().getState();
+ boolean shouldSpring = propertySetter instanceof PendingAnimation
+ && fromState == HINT_STATE && state == NORMAL;
+ if (shouldSpring) {
+ ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
+ mWorkspace, mNewScale));
+ } else {
+ propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+ }
setPivotToScaleWithWorkspace(hotseat);
setPivotToScaleWithWorkspace(qsbScaleView);
float hotseatScale = hotseatScaleAndTranslation.scale;
- Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
- scaleInterpolator);
- propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
- hotseatScaleInterpolator);
- propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
- hotseatScaleInterpolator);
+ if (shouldSpring) {
+ PendingAnimation pa = (PendingAnimation) propertySetter;
+ pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+ pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
+ qsbScaleAndTranslation.scale));
+ } else {
+ Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
+ scaleInterpolator);
+ propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+ hotseatScaleInterpolator);
+ propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
+ hotseatScaleInterpolator);
+ }
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
@@ -191,4 +214,24 @@
pageAlpha, fadeInterpolator);
}
}
+
+ /**
+ * Returns a spring based animator for the scale property of {@param v}.
+ */
+ public static ValueAnimator getSpringScaleAnimator(Launcher launcher, View v, float scale) {
+ ResourceProvider rp = DynamicResource.provider(launcher);
+ float damping = rp.getFloat(R.dimen.hint_scale_damping_ratio);
+ float stiffness = rp.getFloat(R.dimen.hint_scale_stiffness);
+ float velocityPxPerS = rp.getDimension(R.dimen.hint_scale_velocity_dp_per_s);
+
+ return new SpringAnimationBuilder(v.getContext())
+ .setStiffness(stiffness)
+ .setDampingRatio(damping)
+ .setMinimumVisibleChange(MIN_VISIBLE_CHANGE_SCALE)
+ .setEndValue(scale)
+ .setStartValue(SCALE_PROPERTY.get(v))
+ .setStartVelocity(velocityPxPerS)
+ .build(v, SCALE_PROPERTY);
+
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 1d6bb62..c989e7b 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -16,9 +16,14 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
@@ -37,7 +42,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
-import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -54,7 +58,6 @@
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.pm.UserCache;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -195,7 +198,7 @@
}
private void resetWorkProfile() {
- mWorkModeSwitch.refresh();
+ mWorkModeSwitch.update(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
mAH[AdapterHolder.WORK].setupOverlay();
mAH[AdapterHolder.WORK].applyPadding();
}
@@ -431,11 +434,22 @@
}
private void setupWorkToggle() {
- mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
- R.layout.work_mode_switch, this, false);
- this.addView(mWorkModeSwitch);
- mWorkModeSwitch.setInsets(mInsets);
- mWorkModeSwitch.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+ if (Utilities.ATLEAST_P) {
+ mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
+ R.layout.work_mode_switch, this, false);
+ this.addView(mWorkModeSwitch);
+ mWorkModeSwitch.setInsets(mInsets);
+ mWorkModeSwitch.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ View overlay = mAH[AdapterHolder.WORK].getOverlayView();
+ int v = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? GONE : VISIBLE;
+ overlay.findViewById(R.id.work_apps_paused_title).setVisibility(v);
+ overlay.findViewById(R.id.work_apps_paused_content).setVisibility(v);
}
private void replaceRVContainer(boolean showTabs) {
@@ -470,7 +484,9 @@
}
reset(true /* animate */);
if (mWorkModeSwitch != null) {
- mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK);
+ mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK
+ && mAllAppsStore.hasModelFlag(
+ FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
}
}
@@ -580,15 +596,8 @@
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
int searchViewId = getSearchView().getId();
addSpringView(searchViewId);
-
finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
- new DynamicAnimation.OnAnimationEndListener() {
- @Override
- public void onAnimationEnd(DynamicAnimation animation,
- boolean canceled, float value, float velocity) {
- removeSpringView(searchViewId);
- }
- });
+ (anim, canceled, value, velocity) -> removeSpringView(searchViewId));
shouldSpring = false;
}
@@ -647,7 +656,7 @@
void setupOverlay() {
if (!mIsWork || recyclerView == null) return;
- boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
+ boolean workDisabled = mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED);
if (mWorkDisabled == workDisabled) return;
recyclerView.setContentDescription(workDisabled ? mLauncher.getString(
R.string.work_apps_paused_content_description) : null);
@@ -655,10 +664,12 @@
recyclerView.setItemAnimator(new DefaultItemAnimator());
if (workDisabled) {
overlayView.setAlpha(0);
- appsList.updateItemFilter((info, cn) -> false);
recyclerView.addAutoSizedOverlay(overlayView);
overlayView.animate().alpha(1).withEndAction(
- () -> recyclerView.setItemAnimator(null)).start();
+ () -> {
+ appsList.updateItemFilter((info, cn) -> false);
+ recyclerView.setItemAnimator(null);
+ }).start();
} else if (mInfoMatcher != null) {
appsList.updateItemFilter(mInfoMatcher);
overlayView.animate().alpha(0).withEndAction(() -> {
@@ -671,8 +682,12 @@
void applyPadding() {
if (recyclerView != null) {
- int bottomOffset =
- mWorkModeSwitch != null && mIsWork ? mWorkModeSwitch.getHeight() : 0;
+ Resources res = getResources();
+ int switchH = res.getDimensionPixelSize(R.dimen.work_profile_footer_padding) * 2
+ + mInsets.bottom + Utilities.calculateTextHeight(
+ res.getDimension(R.dimen.work_profile_footer_text_size));
+
+ int bottomOffset = mWorkModeSwitch != null && mIsWork ? switchH : 0;
recyclerView.setPadding(padding.left, padding.top, padding.right,
padding.bottom + bottomOffset);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index b11312c..3ae0a18 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -52,6 +52,7 @@
private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>();
private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
+ private int mModelFlags;
private int mDeferUpdatesFlags = 0;
private boolean mUpdatePending = false;
@@ -63,11 +64,21 @@
/**
* Sets the current set of apps.
*/
- public void setApps(AppInfo[] apps) {
+ public void setApps(AppInfo[] apps, int flags) {
mApps = apps;
+ mModelFlags = flags;
notifyUpdate();
}
+ /**
+ * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED
+ * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION
+ * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION
+ */
+ public boolean hasModelFlag(int mask) {
+ return (mModelFlags & mask) != 0;
+ }
+
public AppInfo getApp(ComponentKey key) {
mTempInfo.componentName = key.componentName;
mTempInfo.user = key.user;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 99ed0ad..a9b030e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -14,7 +14,6 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,7 +36,6 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -75,7 +73,6 @@
private ScrimView mScrimView;
private final Launcher mLauncher;
- private final boolean mIsDarkTheme;
private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
@@ -98,7 +95,6 @@
mShiftRange = mLauncher.getDeviceProfile().heightPx;
mProgress = 1f;
- mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
mLauncher.addOnDeviceProfileChangeListener(this);
}
@@ -137,16 +133,6 @@
if (mPlugin != null) {
mPlugin.setProgress(progress);
}
-
- // Use a light system UI (dark icons) if all apps is behind at least half of the
- // status bar.
- boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop())
- <= mLauncher.getDeviceProfile().getInsets().top / 2f;
- if (forceChange) {
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
- } else {
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
- }
}
public float getProgress() {
@@ -173,7 +159,9 @@
StateAnimationConfig config, PendingAnimation builder) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
- setAlphas(toState, config, builder);
+ if (!config.onlyPlayAtomicComponent()) {
+ setAlphas(toState, config, builder);
+ }
// Fail fast
onProgressAnimationEnd();
return;
@@ -189,7 +177,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/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 6692af5..4567ee6 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -15,11 +15,8 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
-
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Process;
@@ -59,7 +56,6 @@
public WorkModeSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
init();
-
}
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -73,9 +69,7 @@
}
@Override
- public void setChecked(boolean checked) {
-
- }
+ public void setChecked(boolean checked) { }
@Override
public void toggle() {
@@ -84,20 +78,17 @@
trySetQuietModeEnabledToAllProfilesAsync(isChecked());
}
- private void setCheckedInternal(boolean checked) {
- super.setChecked(checked);
+ /**
+ * Sets the enabled or disabled state of the button
+ * @param isChecked
+ */
+ public void update(boolean isChecked) {
+ super.setChecked(isChecked);
setCompoundDrawablesRelativeWithIntrinsicBounds(
- checked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
- }
-
- public void refresh() {
- if (!shouldShowWorkSwitch()) return;
- UserCache userManager = UserCache.INSTANCE.get(getContext());
- setCheckedInternal(!userManager.isAnyProfileQuietModeEnabled());
+ isChecked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
setEnabled(true);
}
-
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
@@ -116,12 +107,6 @@
return super.onTouchEvent(ev);
}
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- this.setVisibility(shouldShowWorkSwitch() ? VISIBLE : GONE);
- }
-
private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
}
@@ -138,13 +123,12 @@
* Animates in/out work profile toggle panel based on the tab user is on
*/
public void setWorkTabVisible(boolean workTabVisible) {
- if (!shouldShowWorkSwitch()) return;
clearAnimation();
if (workTabVisible) {
setVisibility(VISIBLE);
setAlpha(0);
animate().alpha(1).start();
- showTipifNeeded();
+ showTipIfNeeded();
} else {
animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
}
@@ -201,16 +185,10 @@
}
}
- private boolean shouldShowWorkSwitch() {
- return Utilities.ATLEAST_P && (hasShortcutsPermission(getContext())
- || getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
- == PackageManager.PERMISSION_GRANTED);
- }
-
/**
* Shows a work tip on the Nth work tab open
*/
- public void showTipifNeeded() {
+ public void showTipIfNeeded() {
Context context = getContext();
SharedPreferences prefs = Utilities.getPrefs(context);
int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index 06d0f1c..6ea38ec 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -35,6 +35,7 @@
private final FlingAnimation mFlingAnim;
private SpringAnimation mSpringAnim;
+ private final boolean mSkipFlingAnim;
private float mTargetPosition;
@@ -57,6 +58,10 @@
.setMaxValue(maxValue);
mTargetPosition = targetPosition;
+ // We are already past the fling target, so skip it to avoid losing a frame of the spring.
+ mSkipFlingAnim = startPosition <= minValue && startVelocity < 0
+ || startPosition >= maxValue && startVelocity > 0;
+
mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
mSpringAnim = new SpringAnimation(object, property)
.setStartValue(value)
@@ -84,6 +89,9 @@
public void start() {
mFlingAnim.start();
+ if (mSkipFlingAnim) {
+ mFlingAnim.cancel();
+ }
}
public void end() {
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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 118ce0c..4f53d45 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -98,6 +98,10 @@
"FOLDER_NAME_SUGGEST", true,
"Suggests folder names instead of blank text.");
+ public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(
+ "FOLDER_NAME_MAJORITY_RANKING", true,
+ "Suggests folder names based on majority based ranking.");
+
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", true,
"Adds localized title and keyword search and ranking");
@@ -122,7 +126,7 @@
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
"ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
- public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
+ public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
"HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
@@ -173,6 +177,9 @@
"SEPARATE_RECENTS_ACTIVITY", false,
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
+ public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
+ "USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index fdf0ea4..7998c2d 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();
@@ -1332,8 +1337,8 @@
if (d.stateAnnouncer != null) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
- mStatsLogManager
- .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
+ mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+ .log(LAUNCHER_ITEM_DROP_COMPLETED);
}
// 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.logger().withItemInfo(mInfo).log(LAUNCHER_FOLDER_LABEL_UPDATED);
logFolderLabelState();
mFolderName.dispatchBackKey();
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 153d6bc..b40b1e2 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -201,8 +201,7 @@
icon.mActivity = activity;
icon.mDotRenderer = grid.mDotRendererWorkSpace;
- icon.setContentDescription(
- group.getContext().getString(R.string.folder_name_format, folderInfo.title));
+ icon.setContentDescription(icon.getAccessiblityTitle(folderInfo.title));
// Keep the notification dot up to date with the sum of all the content's dots.
FolderDotInfo folderDotInfo = new FolderDotInfo();
@@ -449,8 +448,8 @@
return;
}
mInfo.setTitle(nameInfos[0].getLabel());
- StatsLogManager.newInstance(getContext())
- .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
+ StatsLogManager.newInstance(getContext()).logger().withItemInfo(mInfo)
+ .withInstanceId(instanceId).log(LAUNCHER_FOLDER_LABEL_UPDATED);
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -665,6 +664,7 @@
mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
+ setContentDescription(getAccessiblityTitle(mInfo.title));
invalidate();
requestLayout();
}
@@ -675,13 +675,14 @@
mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
+ setContentDescription(getAccessiblityTitle(mInfo.title));
invalidate();
requestLayout();
}
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
- setContentDescription(getContext().getString(R.string.folder_name_format, title));
+ setContentDescription(getAccessiblityTitle(title));
}
@Override
@@ -775,4 +776,17 @@
public void getWorkspaceVisualDragBounds(Rect bounds) {
getPreviewBounds(bounds);
}
+
+ /**
+ * Returns a formatted accessibility title for folder
+ */
+ public String getAccessiblityTitle(CharSequence title) {
+ int size = mInfo.contents.size();
+ if (size < MAX_NUM_ITEMS_IN_PREVIEW) {
+ return getContext().getString(R.string.folder_name_format_exact, title, size);
+ } else {
+ return getContext().getString(R.string.folder_name_format_overflow, title,
+ MAX_NUM_ITEMS_IN_PREVIEW);
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderNameEditText.java b/src/com/android/launcher3/folder/FolderNameEditText.java
index edf2c70..6038a05 100644
--- a/src/com/android/launcher3/folder/FolderNameEditText.java
+++ b/src/com/android/launcher3/folder/FolderNameEditText.java
@@ -70,8 +70,11 @@
for (int i = 0; i < cnt; i++) {
cInfo[i] = new CompletionInfo(i, i, suggestList.get(i));
}
- post(() -> getContext().getSystemService(InputMethodManager.class)
- .displayCompletions(this, cInfo));
+ // post it to future frame so that onSelectionChanged, onFocusChanged, all other
+ // TextView flag change and IME animation has settled. Ideally, there should be IMM
+ // callback to notify when the IME animation and state handling is finished.
+ postDelayed(() -> getContext().getSystemService(InputMethodManager.class)
+ .displayCompletions(this, cInfo), 40 /* 2~3 frame delay */);
}
/**
diff --git a/src/com/android/launcher3/logging/InstanceIdSequence.java b/src/com/android/launcher3/logging/InstanceIdSequence.java
index a4b7953..ee6a5a4 100644
--- a/src/com/android/launcher3/logging/InstanceIdSequence.java
+++ b/src/com/android/launcher3/logging/InstanceIdSequence.java
@@ -45,6 +45,13 @@
}
/**
+ * Constructs a sequence with identifiers [1, InstanceId.INSTANCE_ID_MAX].
+ */
+ public InstanceIdSequence() {
+ this(InstanceId.INSTANCE_ID_MAX);
+ }
+
+ /**
* Gets the next instance from the sequence. Safe for concurrent use.
* @return new InstanceId
*/
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index f0a9efb..82d61da 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -19,10 +19,10 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom.ItemInfo;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ResourceBasedOverride;
/**
@@ -127,9 +127,53 @@
LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP(522),
@UiEvent(doc = "User is shown All Apps education view.")
- LAUNCHER_ALL_APPS_EDU_SHOWN(523);
- // ADD MORE
+ 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),
+
+ /**
+ * @deprecated LauncherUiChanged.rank field is repurposed to store all apps rank, so no
+ * separate event is required.
+ */
+ @Deprecated
+ @UiEvent(doc = "App launch ranking logged for all apps predictions")
+ LAUNCHER_ALL_APPS_RANKED(552),
+
+ @UiEvent(doc = "App launch ranking logged for hotseat predictions)")
+ LAUNCHER_HOTSEAT_RANKED(553),
+
+ @UiEvent(doc = "User's workspace layout information is snapshot in the background.")
+ LAUNCHER_WORKSPACE_SNAPSHOT(579),
+
+ @UiEvent(doc = "User tapped on the screenshot button on overview)")
+ LAUNCHER_OVERVIEW_ACTIONS_SCREENSHOT(580),
+
+ @UiEvent(doc = "User tapped on the select button on overview)")
+ LAUNCHER_OVERVIEW_ACTIONS_SELECT(581),
+
+ @UiEvent(doc = "User tapped on the share button on overview")
+ LAUNCHER_OVERVIEW_ACTIONS_SHARE(582),
+
+ @UiEvent(doc = "User tapped on the close button in select mode")
+ LAUNCHER_SELECT_MODE_CLOSE(583),
+
+ @UiEvent(doc = "User tapped on the highlight items in select mode")
+ LAUNCHER_SELECT_MODE_ITEM(584);
+
+ // ADD MORE
private final int mId;
LauncherEvent(int id) {
@@ -141,6 +185,91 @@
}
}
+ /**
+ * Launcher specific ranking related events.
+ */
+ public enum LauncherRankingEvent implements EventEnum {
+
+ UNKNOWN(0);
+ // ADD MORE
+
+ private final int mId;
+
+ LauncherRankingEvent(int id) {
+ mId = id;
+ }
+
+ public int getId() {
+ return mId;
+ }
+ }
+
+ /**
+ * Helps to construct and write the log message.
+ */
+ public interface StatsLogger {
+
+ /**
+ * Sets log fields from provided {@link ItemInfo}.
+ */
+ default StatsLogger withItemInfo(ItemInfo itemInfo) {
+ return this;
+ }
+
+
+ /**
+ * Sets {@link InstanceId} of log message.
+ */
+ default StatsLogger withInstanceId(InstanceId instanceId) {
+ return this;
+ }
+
+ /**
+ * Sets rank field of log message.
+ */
+ default StatsLogger withRank(int rank) {
+ return this;
+ }
+
+ /**
+ * Sets source launcher state field of log message.
+ */
+ default StatsLogger withSrcState(int srcState) {
+ return this;
+ }
+
+ /**
+ * Sets destination launcher state field of log message.
+ */
+ default StatsLogger withDstState(int dstState) {
+ return this;
+ }
+
+ /**
+ * Sets the final value for container related fields of log message.
+ *
+ * By default container related fields are derived from {@link ItemInfo}, this method would
+ * override those values.
+ */
+ default StatsLogger withContainerInfo(ContainerInfo containerInfo) {
+ return this;
+ }
+
+ /**
+ * Builds the final message and logs it as {@link EventEnum}.
+ */
+ default void log(EventEnum event) {
+ }
+ }
+
+ /**
+ * Returns new logger object.
+ */
+ public StatsLogger logger() {
+ return new StatsLogger() {
+ };
+ }
+
protected LogStateProvider mStateProvider;
/**
@@ -158,34 +287,16 @@
}
/**
- * Logs a {@link EventEnum}.
+ * 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 event) {
- }
-
- /**
- * Logs an event and accompanying {@link InstanceId}.
- */
- public void log(EventEnum event, InstanceId instanceId) {
- }
-
- /**
- * Logs an event and accompanying {@link ItemInfo}.
- */
- public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
- }
-
- /**
- * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
- */
- public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
- }
-
- /**
- * 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.
- */
- public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
+ public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
+ int position) {
}
/**
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/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 4f349ca..eb5d106 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -35,6 +35,7 @@
import com.android.launcher3.AppFilter;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.pm.PackageInstallInfo;
@@ -73,6 +74,13 @@
private AlphabeticIndexCompat mIndex;
/**
+ * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION
+ * @see Callbacks#FLAG_QUIET_MODE_ENABLED
+ * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION
+ */
+ private int mFlags;
+
+ /**
* Boring constructor.
*/
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
@@ -91,6 +99,33 @@
}
/**
+ * Helper to checking {@link Callbacks#FLAG_HAS_SHORTCUT_PERMISSION}
+ */
+ public boolean hasShortcutHostPermission() {
+ return (mFlags & Callbacks.FLAG_HAS_SHORTCUT_PERMISSION) != 0;
+ }
+
+ /**
+ * Sets or clears the provided flag
+ */
+ public void setFlags(int flagMask, boolean enabled) {
+ if (enabled) {
+ mFlags |= flagMask;
+ } else {
+ mFlags &= ~flagMask;
+ }
+ mDataChanged = true;
+ }
+
+ /**
+ * Returns the model flags
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+
+ /**
* Add the supplied ApplicationInfo objects to the list, and enqueue it into the
* list to broadcast when notify() is called.
*
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index ab921ea..8b0ef7b 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -96,7 +96,8 @@
public void bindAllApps() {
// shallow copy
AppInfo[] apps = mBgAllAppsList.copyData();
- executeCallbacksTask(c -> c.bindAllApplications(apps), mUiExecutor);
+ int flags = mBgAllAppsList.getFlags();
+ executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}
public abstract void bindWidgets();
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 7ce970d..9013cba 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -117,7 +117,8 @@
public void bindApplicationsIfNeeded() {
if (mAllAppsList.getAndResetChangeFlag()) {
AppInfo[] apps = mAllAppsList.copyData();
- scheduleCallbackTask(c -> c.bindAllApplications(apps));
+ int flags = mAllAppsList.getFlags();
+ scheduleCallbackTask(c -> c.bindAllApplications(apps, flags));
}
}
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 2522a49..9bef847 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -98,9 +98,11 @@
public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
/**
- * True if the launcher has permission to access deep shortcuts.
+ * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION
+ * @see Callbacks#FLAG_QUIET_MODE_ENABLED
+ * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION
*/
- public boolean hasShortcutHostPermission;
+ public int flags;
/**
* Maps all launcher activities to counts of their shortcuts.
@@ -347,6 +349,13 @@
}
public interface Callbacks {
+ // If the launcher has permission to access deep shortcuts.
+ int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
+ // If quiet mode is enabled for any user
+ int FLAG_QUIET_MODE_ENABLED = 1 << 1;
+ // If launcher can change quiet mode
+ int FLAG_QUIET_MODE_CHANGE_PERMISSION = 1 << 2;
+
/**
* Returns the page number to bind first, synchronously if possible or -1
*/
@@ -370,7 +379,7 @@
void executeOnNextDraw(ViewOnDrawExecutor executor);
void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
- void bindAllApplications(AppInfo[] apps);
+ void bindAllApplications(AppInfo[] apps, int flags);
/**
* Binds predicted appInfos at at available prediction slots.
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 4bfabb0..25a2c69 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -32,7 +32,6 @@
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -53,13 +52,13 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
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,75 +247,78 @@
/** 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;
}
- private static void insertEntryInDb(SQLiteDatabase db, Context context,
- ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry, String srcTableName,
- String destTableName) {
- int id = -1;
- switch (entry.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
- for (DbEntry e : entriesFromSrcDb) {
- if (TextUtils.equals(e.mIntent, entry.mIntent)) {
- id = e.id;
- }
- }
+ private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
+ String srcTableName, String destTableName) {
+ int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
- break;
+ if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ for (int itemId : entry.mFolderItems.values()) {
+ copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
}
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
- for (DbEntry e : entriesFromSrcDb) {
- if (e.mFolderItems.size() == entry.mFolderItems.size()
- && e.mFolderItems.containsAll(entry.mFolderItems)) {
- id = e.id;
- }
- }
- break;
- }
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
- for (DbEntry e : entriesFromSrcDb) {
- if (TextUtils.equals(e.mProvider, entry.mProvider)) {
- id = e.id;
- break;
- }
- }
- break;
- }
- default:
- return;
}
+ }
- Cursor c = db.query(srcTableName, null, LauncherSettings.Favorites._ID + " = '" + id + "'",
+ private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ DbEntry entry, String srcTableName, String destTableName) {
+ return copyEntryAndUpdate(db, context, entry, -1, -1, srcTableName, destTableName);
+ }
+
+ private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ int id, int folderId, String srcTableName, String destTableName) {
+ return copyEntryAndUpdate(db, context, null, id, folderId, srcTableName, destTableName);
+ }
+
+ private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ DbEntry entry, int id, int folderId, String srcTableName, String destTableName) {
+ int newId = -1;
+ Cursor c = db.query(srcTableName, null,
+ LauncherSettings.Favorites._ID + " = '" + (entry != null ? entry.id : id) + "'",
null, null, null, null);
-
while (c.moveToNext()) {
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(c, values);
- entry.updateContentValues(values);
- values.put(LauncherSettings.Favorites._ID,
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
- LauncherSettings.Settings.EXTRA_VALUE));
+ if (entry != null) {
+ entry.updateContentValues(values);
+ } else {
+ values.put(LauncherSettings.Favorites.CONTAINER, folderId);
+ }
+ newId = LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
+ LauncherSettings.Settings.EXTRA_VALUE);
+ values.put(LauncherSettings.Favorites._ID, newId);
db.insert(destTableName, null, values);
}
c.close();
+ return newId;
}
- private static void removeEntryFromDb(SQLiteDatabase db, String tableName, IntArray entryId) {
+ private static void removeEntryFromDb(SQLiteDatabase db, String tableName, IntArray entryIds) {
db.delete(tableName,
- Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryId), null);
+ Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryIds), null);
}
private static HashSet<String> getValidPackages(Context context) {
@@ -381,8 +383,8 @@
continue;
}
if (findPlacement(entry)) {
- insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry,
- mSrcReader.mTableName, mDestReader.mTableName);
+ insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
+ mDestReader.mTableName);
iterator.remove();
}
}
@@ -394,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,
@@ -451,8 +453,8 @@
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
- insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry,
- mSrcReader.mTableName, mDestReader.mTableName);
+ insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
+ mDestReader.mTableName);
mOccupied.markCells(entry, true);
}
}
@@ -669,10 +671,11 @@
int total = 0;
while (c.moveToNext()) {
try {
+ int id = c.getInt(0);
String intent = c.getString(1);
verifyIntent(intent);
total++;
- entry.mFolderItems.add(intent);
+ entry.mFolderItems.put(intent, id);
} catch (Exception e) {
removeEntryFromDb(mDb, mTableName, IntArray.wrap(c.getInt(0)));
}
@@ -711,7 +714,7 @@
private String mIntent;
private String mProvider;
- private Set<String> mFolderItems = new HashSet<>();
+ private Map<String, Integer> mFolderItems = new HashMap<>();
/** Comparator according to the reading order */
@Override
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 244de96..165d1ea 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -65,7 +65,7 @@
private static final String TAG = "LoaderCursor";
- public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+ public final LongSparseArray<UserHandle> allUsers;
private final Uri mContentUri;
private final Context mContext;
@@ -100,9 +100,11 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app) {
+ public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app,
+ UserManagerState userManagerState) {
super(cursor);
+ allUsers = userManagerState.allUsers;
mContentUri = contentUri;
mContext = app.getContext();
mIconCache = app.getIconCache();
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d05d70b..f2073ef 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -17,6 +17,9 @@
package com.android.launcher3.model;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
@@ -35,6 +38,7 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.UserHandle;
@@ -120,6 +124,8 @@
private final InstallSessionHelper mSessionHelper;
private final IconCache mIconCache;
+ private final UserManagerState mUserManagerState = new UserManagerState();
+
private boolean mStopped;
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
@@ -333,7 +339,8 @@
Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
final LoaderCursor c = new LoaderCursor(
- contentResolver.query(contentUri, null, null, null, null), contentUri, mApp);
+ contentResolver.query(contentUri, null, null, null, null), contentUri, mApp,
+ mUserManagerState);
Map<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
@@ -352,12 +359,13 @@
LauncherSettings.Favorites.OPTIONS);
final LongSparseArray<UserHandle> allUsers = c.allUsers;
- final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
+
+ mUserManagerState.init(mUserCache, mUserManager);
+
for (UserHandle user : mUserCache.getUserProfiles()) {
long serialNo = mUserCache.getSerialNumberForUser(user);
allUsers.put(serialNo, user);
- quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
boolean userUnlocked = mUserManager.isUserUnlocked(user);
@@ -404,8 +412,8 @@
continue;
}
- int disabledState = quietMode.get(c.serialNumber) ?
- WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
+ int disabledState = mUserManagerState.isUserQuiet(c.serialNumber)
+ ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
ComponentName cn = intent.getComponent();
targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
@@ -862,8 +870,8 @@
for (ComponentKey key : componentKeys) {
l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
if (l.size() == 0) continue;
- boolean quietMode = mUserManager.isQuietModeEnabled(key.user);
- AppInfo info = new AppInfo(l.get(0), key.user, quietMode);
+ AppInfo info = new AppInfo(l.get(0), key.user,
+ mUserManagerState.isUserQuiet(key.user));
mBgDataModel.cachedPredictedItems.add(info);
mIconCache.getTitleAndIcon(info, false);
}
@@ -883,7 +891,7 @@
if (apps == null || apps.isEmpty()) {
return allActivityList;
}
- boolean quietMode = mUserManager.isQuietModeEnabled(user);
+ boolean quietMode = mUserManagerState.isUserQuiet(user);
// Create the ApplicationInfos
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
@@ -905,11 +913,19 @@
List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
item.componentName.getPackageName(), item.user);
for (LauncherActivityInfo info : l) {
- boolean quietMode = mUserManager.isQuietModeEnabled(item.user);
+ boolean quietMode = mUserManagerState.isUserQuiet(item.user);
mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
}
}
+ mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
+ mUserManagerState.isAnyProfileQuietModeEnabled());
+ mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
+ hasShortcutsPermission(mApp.getContext()));
+ mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
+ mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ == PackageManager.PERMISSION_GRANTED);
+
mBgAllAppsList.getAndResetChangeFlag();
return allActivityList;
}
@@ -917,8 +933,8 @@
private List<ShortcutInfo> loadDeepShortcuts() {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
mBgDataModel.deepShortcutMap.clear();
- mBgDataModel.hasShortcutHostPermission = hasShortcutsPermission(mApp.getContext());
- if (mBgDataModel.hasShortcutHostPermission) {
+
+ if (mBgAllAppsList.hasShortcutHostPermission()) {
for (UserHandle user : mUserCache.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 2fa6051..7cd467e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON;
@@ -41,6 +42,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap;
@@ -149,14 +151,21 @@
if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
appsList.updateDisabledFlags(matcher, flagOp);
break;
- case OP_USER_AVAILABILITY_CHANGE:
- flagOp = context.getSystemService(UserManager.class).isQuietModeEnabled(mUser)
+ case OP_USER_AVAILABILITY_CHANGE: {
+ UserManagerState ums = new UserManagerState();
+ ums.init(UserCache.INSTANCE.get(context),
+ context.getSystemService(UserManager.class));
+ flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
+
+ // We are not synchronizing here, as int operations are atomic
+ appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
+ }
}
bindApplicationsIfNeeded();
diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java
new file mode 100644
index 0000000..3a4206c
--- /dev/null
+++ b/src/com/android/launcher3/model/UserManagerState.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.model;
+
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.LongSparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.launcher3.pm.UserCache;
+
+/**
+ * Utility class to manager store and user manager state at any particular time
+ */
+public class UserManagerState {
+
+ public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+
+ private final LongSparseArray<Boolean> mQuietUsersSerialNoMap = new LongSparseArray<>();
+ private final SparseBooleanArray mQuietUsersHashCodeMap = new SparseBooleanArray();
+
+ /**
+ * Initialises the state values for all users
+ */
+ public void init(UserCache userCache, UserManager userManager) {
+ for (UserHandle user : userCache.getUserProfiles()) {
+ long serialNo = userCache.getSerialNumberForUser(user);
+ boolean isUserQuiet = userManager.isQuietModeEnabled(user);
+ allUsers.put(serialNo, user);
+ mQuietUsersHashCodeMap.put(user.hashCode(), isUserQuiet);
+ mQuietUsersSerialNoMap.put(serialNo, isUserQuiet);
+ }
+ }
+
+ /**
+ * Returns true if quiet mode is enabled for the provided user
+ */
+ public boolean isUserQuiet(long serialNo) {
+ return mQuietUsersSerialNoMap.get(serialNo);
+ }
+
+ /**
+ * Returns true if quiet mode is enabled for the provided user
+ */
+ public boolean isUserQuiet(UserHandle user) {
+ return mQuietUsersHashCodeMap.get(user.hashCode());
+ }
+
+ /**
+ * Returns true if any user profile has quiet mode enabled.
+ */
+ public boolean isAnyProfileQuietModeEnabled() {
+ for (int i = mQuietUsersHashCodeMap.size() - 1; i >= 0; i--) {
+ if (mQuietUsersHashCodeMap.valueAt(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 096743a..8f577b5 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -22,6 +22,8 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
+import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
@@ -205,6 +207,7 @@
return getDefaultItemInfoBuilder()
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
.setRank(rank)
+ .setAttribute(fromCustom ? MANUAL_LABEL : SUGGESTED_LABEL)
.setContainerInfo(getContainerInfo())
.build();
}
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..835f72d 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -107,8 +107,8 @@
try {
intent.send(null, 0, null, null, null, null, activityOptions);
launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
- launcher.getStatsLogManager()
- .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo.buildProto());
+ launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
+ .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index f723256..5aab41a 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.Intent;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -107,22 +106,6 @@
}
/**
- * Returns true if any user profile has quiet mode enabled.
- */
- public boolean isAnyProfileQuietModeEnabled() {
- List<UserHandle> userProfiles = getUserProfiles();
- for (UserHandle userProfile : userProfiles) {
- if (Process.myUserHandle().equals(userProfile)) {
- continue;
- }
- if (mUserManager.isQuietModeEnabled(userProfile)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* @see UserManager#getSerialNumberForUser(UserHandle)
*/
public long getSerialNumberForUser(UserHandle user) {
@@ -160,16 +143,4 @@
List<UserHandle> users = mUserManager.getUserProfiles();
return users == null ? Collections.emptyList() : users;
}
-
- /**
- * Returns true is there is at least one user profile enabled
- */
- public boolean hasWorkProfile() {
- synchronized (this) {
- if (mUsers != null) {
- return mUsers.size() > 1;
- }
- }
- return getUserProfiles().size() > 1;
- }
}
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..61829c0 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -77,8 +77,8 @@
@Override
public void onClick(View view) {
AbstractFloatingView.closeAllOpenViews(mTarget);
- mTarget.getStatsLogManager()
- .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo.buildProto());
+ mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
+ .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP);
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..fd292a3 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -119,9 +119,8 @@
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().logger().withItemInfo(mItemInfo)
+ .log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
}
}
@@ -142,9 +141,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());
+ mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
+ .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 4a15af1..21ad275 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -296,8 +296,8 @@
}
@Override
- public void bindAllApplications(AppInfo[] apps) {
- mAppsView.getAppsStore().setApps(apps);
+ public void bindAllApplications(AppInfo[] apps, int flags) {
+ mAppsView.getAppsStore().setApps(apps, flags);
}
public PopupDataProvider getPopupDataProvider() {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index 9ea8436..b8a184f 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -39,8 +39,18 @@
}
@Override
+ protected float getDepthUnchecked(Context context) {
+ return 0.15f;
+ }
+
+ @Override
+ public float getOverviewScrimAlpha(Launcher launcher) {
+ return 0.4f;
+ }
+
+ @Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new ScaleAndTranslation(0.9f, 0, 0);
+ return new ScaleAndTranslation(0.92f, 0, 0);
}
@Override
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..8486666 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,8 @@
if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
folder.animateOpen();
+ StatsLogManager.newInstance(v.getContext()).logger().withItemInfo(folder.mInfo)
+ .log(LAUNCHER_FOLDER_OPEN);
}
}
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/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 7270ce2..4fa658e 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -174,7 +174,7 @@
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- mLauncher.getStatsLogManager().log(LAUNCHER_WORKSPACE_LONGPRESS);
+ mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
} else {
cancelLongPress();
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 1620289..d4e074c 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -37,13 +37,15 @@
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";
+ public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
/**
* 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 +57,7 @@
HOME_BOUNCE_COUNT,
SHELF_BOUNCE_COUNT,
ALL_APPS_COUNT,
+ HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventCountKey {}
@@ -65,6 +68,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);
}
@@ -96,6 +100,13 @@
}
/**
+ * Marks on-boarding preference boolean at true
+ */
+ public void markChecked(String flag) {
+ mSharedPrefs.edit().putBoolean(flag, true).apply();
+ }
+
+ /**
* Add 1 to the given event count, if we haven't already reached the max count.
* @return Whether we have now reached the max count.
*/
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index ddde6d3..86f3431 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,7 +16,6 @@
package com.android.launcher3.util;
-import static android.content.pm.PackageInstaller.SessionInfo;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.app.AppOpsManager;
@@ -45,8 +44,6 @@
import android.util.Pair;
import android.widget.Toast;
-import androidx.annotation.NonNull;
-
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -348,15 +345,4 @@
}
return false;
}
-
- /**
- * Returns the created time in millis of given session info. Returns 0 if not available.
- */
- public static long getSessionCreatedTimeInMillis(@NonNull final SessionInfo info) {
- try {
- return (long) SessionInfo.class.getDeclaredMethod("getCreatedMillis").invoke(info);
- } catch (Exception e) {
- return 0;
- }
- }
}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 53cc157..275c024 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -30,7 +30,7 @@
// Various UI states in increasing order of priority
public static final int UI_STATE_BASE_WINDOW = 0;
- public static final int UI_STATE_ALL_APPS = 1;
+ public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_OVERVIEW = 3;
@@ -61,25 +61,38 @@
// Apply the state flags in priority order
int newFlags = oldFlags;
for (int stateFlag : mStates) {
- if (Utilities.ATLEAST_OREO) {
- if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
- newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
- newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
- }
- }
-
- if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
- newFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
- newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- }
+ newFlags = getSysUiVisibilityFlags(stateFlag, newFlags);
}
if (newFlags != oldFlags) {
mWindow.getDecorView().setSystemUiVisibility(newFlags);
}
}
+ /**
+ * Returns the sysui visibility for the base layer
+ */
+ public int getBaseSysuiVisibility() {
+ return getSysUiVisibilityFlags(
+ mStates[UI_STATE_BASE_WINDOW], mWindow.getDecorView().getSystemUiVisibility());
+ }
+
+ private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
+ if (Utilities.ATLEAST_OREO) {
+ if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+ currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+ currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ }
+
+ if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
+ currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
+ currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ return currentVisibility;
+ }
+
@Override
public String toString() {
return "mStates=" + Arrays.toString(mStates);
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()));
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f54edc2..b010b4b 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -90,13 +90,23 @@
}
};
- // Touch is being dispatched through the normal view dispatch system
- private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
+ // Touch coming from normal view system is being dispatched.
+ private static final int TOUCH_DISPATCHING_FROM_VIEW = 1 << 0;
// Touch is being dispatched through the normal view dispatch system, and started at the
- // system gesture region
- private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
- // Touch is being dispatched through a proxy from InputMonitor
- private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
+ // system gesture region. In this case we prevent internal gesture handling and only allow
+ // normal view event handling.
+ private static final int TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION = 1 << 1;
+ // Touch coming from InputMonitor proxy is being dispatched 'only to gestures'. Note that both
+ // this and view-system can be active at the same time where view-system would go to the views,
+ // and this would go to the gestures.
+ // Note that this is not set when events are coming from proxy, but going through full dispatch
+ // process (both views and gestures) to allow view-system to easily take over in case it
+ // comes later.
+ private static final int TOUCH_DISPATCHING_FROM_PROXY = 1 << 2;
+ // ACTION_DOWN has been dispatched to child views and ACTION_UP or ACTION_CANCEL is pending.
+ // Note that the event source can either be view-dispatching or proxy-dispatching based on if
+ // TOUCH_DISPATCHING_VIEW is present or not.
+ private static final int TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS = 1 << 3;
protected final float[] mTmpXY = new float[2];
protected final float[] mTmpRectPoints = new float[4];
@@ -204,7 +214,8 @@
protected boolean findActiveController(MotionEvent ev) {
mActiveController = null;
- if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
+ if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
+ | TOUCH_DISPATCHING_FROM_PROXY)) == 0) {
// Only look for controllers if we are not dispatching from gesture area and proxy is
// not active
mActiveController = findControllerToHandleTouch(ev);
@@ -283,19 +294,28 @@
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case ACTION_DOWN: {
- mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
+ if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
+ // Cancel the previous touch
+ int action = ev.getAction();
+ ev.setAction(ACTION_CANCEL);
+ super.dispatchTouchEvent(ev);
+ ev.setAction(action);
+ }
+ mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW
+ | TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
if (isEventInLauncher(ev)) {
- mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
} else {
- mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
+ mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
}
break;
}
case ACTION_CANCEL:
case ACTION_UP:
- mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
- mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
break;
}
super.dispatchTouchEvent(ev);
@@ -305,43 +325,53 @@
}
/**
- * Called before we are about to receive proxy events.
- *
- * @return false if we can't handle proxy at this time
- */
- public boolean prepareProxyEventStarting() {
- mProxyTouchController = null;
- if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
- // We are already dispatching using view system and have an active controller, we can't
- // handle another controller.
-
- // This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
- // to be safe
- mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
- return false;
- }
-
- mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
- return true;
- }
-
- /**
* Proxies the touch events to the gesture handlers
*/
- public boolean proxyTouchEvent(MotionEvent ev) {
- boolean handled;
- if (mProxyTouchController != null) {
- handled = mProxyTouchController.onControllerTouchEvent(ev);
+ public boolean proxyTouchEvent(MotionEvent ev, boolean allowViewDispatch) {
+ int actionMasked = ev.getActionMasked();
+ boolean isViewDispatching = (mTouchDispatchState & TOUCH_DISPATCHING_FROM_VIEW) != 0;
+
+ // Only do view dispatch if another view-dispatching is not running, or we already started
+ // proxy-dispatching before. Note that view-dispatching can always take over the proxy
+ // dispatching at anytime, but not vice-versa.
+ allowViewDispatch = allowViewDispatch && !isViewDispatching
+ && (actionMasked == ACTION_DOWN
+ || ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0));
+
+ if (allowViewDispatch) {
+ mTouchDispatchState |= TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
+ super.dispatchTouchEvent(ev);
+
+ if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+ }
+ return true;
} else {
- mProxyTouchController = findControllerToHandleTouch(ev);
- handled = mProxyTouchController != null;
+ boolean handled;
+ if (mProxyTouchController != null) {
+ handled = mProxyTouchController.onControllerTouchEvent(ev);
+ } else {
+ if (actionMasked == ACTION_DOWN) {
+ if (isViewDispatching && mActiveController != null) {
+ // A controller is already active, we can't initiate our own controller
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+ } else {
+ // We will control the handler via proxy
+ mTouchDispatchState |= TOUCH_DISPATCHING_FROM_PROXY;
+ }
+ }
+ if ((mTouchDispatchState & TOUCH_DISPATCHING_FROM_PROXY) != 0) {
+ mProxyTouchController = findControllerToHandleTouch(ev);
+ }
+ handled = mProxyTouchController != null;
+ }
+ if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
+ mProxyTouchController = null;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+ }
+ return handled;
}
- int action = ev.getAction();
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- mProxyTouchController = null;
- mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
- }
- return handled;
}
/**
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 5431ba1..d558781 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -86,7 +86,7 @@
return false;
}
if (item.mEventId.getId() > 0) {
- mLauncher.getStatsLogManager().log(item.mEventId);
+ mLauncher.getStatsLogManager().logger().log(item.mEventId);
}
if (item.mClickListener.onLongClick(view)) {
close(true);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index a2c7d14..22faf97 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -53,6 +54,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@@ -77,7 +79,6 @@
import java.util.List;
-
/**
* Simple scrim which draws a flat color
*/
@@ -115,6 +116,7 @@
private final WallpaperColorInfo mWallpaperColorInfo;
private final AccessibilityManager mAM;
protected final int mEndScrim;
+ protected final boolean mIsScrimDark;
private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
new StateListener<LauncherState>() {
@@ -156,6 +158,7 @@
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
mMaxScrimAlpha = 0.7f;
@@ -233,6 +236,7 @@
mProgress = progress;
stopDragHandleEducationAnim();
updateColors();
+ updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
@@ -245,6 +249,17 @@
mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
}
+ protected void updateSysUiColors() {
+ // Use a light system UI (dark icons) if all apps is behind at least half of the
+ // status bar.
+ boolean forceChange = mProgress <= 0.1f;
+ if (forceChange) {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+ } else {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+ }
+ }
+
protected void updateDragHandleAlpha() {
if (mDragHandle != null) {
mDragHandle.setAlpha(mDragHandleAlpha);