Merge "Updating Chips UI" into sc-dev
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 9c14b85..7431551 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -41,7 +41,7 @@
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
index 2766acf..6253b41 100644
--- a/quickstep/protos_overrides/launcher_atom_extension.proto
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -31,4 +31,5 @@
// Represents on-device search result container.
message DeviceSearchResultContainer{
+ optional int32 query_length = 1;
}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index cbe0eb0..edcd0a2 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -355,10 +355,6 @@
// populating workspace.
// TODO: Find a better place for this
WellbeingModel.INSTANCE.get(this);
-
- if (mTaskbarController != null) {
- mTaskbarController.onHotseatUpdated();
- }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 034d51f..588d676 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -56,17 +56,22 @@
return mHandler;
}
- // Called only in R+ platform
+ // Called only in S+ platform
@BinderThread
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+ public void onAnimationStart(
+ int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
mAnimationResult = new AnimationResult(() -> {
UI_HELPER_EXECUTOR.execute(runnable);
mAnimationResult = null;
});
- onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
+ onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+ mAnimationResult);
};
if (mStartAtFrontOfQueue) {
postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -75,6 +80,14 @@
}
}
+ // Called only in R platform
+ @BinderThread
+ public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+ onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
+ new RemoteAnimationTargetCompat[0], runnable);
+ }
+
// Called only in Q platform
@BinderThread
@Deprecated
@@ -88,8 +101,11 @@
*/
@UiThread
public abstract void onCreateAnimation(
+ int transit,
RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result);
@UiThread
private void finishExistingAnimation() {
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 876cabc..c4b6961 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -866,8 +866,10 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
@@ -880,7 +882,8 @@
// If launcher is not resumed, wait until new async-frame after resume
mLauncher.addOnResumeCallback(() ->
postAsyncCallback(mHandler, () ->
- onCreateAnimation(appTargets, wallpaperTargets, result)));
+ onCreateAnimation(transit, appTargets, wallpaperTargets,
+ nonAppTargets, result)));
return;
}
@@ -964,8 +967,10 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
index da2aee4..03cc28e 100644
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -26,7 +26,9 @@
*/
public interface WrappedAnimationRunnerImpl {
Handler getHandler();
- void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result);
}
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
index 1753b62..1e1631b 100644
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -46,11 +46,15 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result) {
R animationRunnerImpl = mImpl.get();
if (animationRunnerImpl != null) {
- animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+ animationRunnerImpl.onCreateAnimation(transit, appTargets, wallpaperTargets,
+ nonAppTargets, result);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
index c968de9..3a1a2f7 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -29,7 +29,6 @@
*/
public class HotseatEduActivity extends Activity {
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -49,7 +48,7 @@
@Override
public boolean init(BaseActivity activity, boolean alreadyOnHome) {
QuickstepLauncher launcher = (QuickstepLauncher) activity;
- if (launcher != null && launcher.getHotseatPredictionController() != null) {
+ if (launcher != null) {
launcher.getHotseatPredictionController().showEdu();
}
return false;
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
deleted file mode 100644
index 20e1edc..0000000
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
+++ /dev/null
@@ -1,128 +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.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() {
- Calendar cal = Calendar.getInstance();
- String fName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) % 10);
- if (fName.equals(mFileName)) return mCurrentWriter;
-
- 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/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index aa6601b..5e81fba 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -20,12 +20,12 @@
import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
-import android.os.Process;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +39,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
@@ -70,7 +69,11 @@
*/
public class HotseatPredictionController implements DragController.DragListener,
SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
- DragSource {
+ DragSource, ViewGroup.OnHierarchyChangeListener {
+
+ private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+ private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+ private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
private int mHotSeatItemsCount;
@@ -80,8 +83,7 @@
private List<ItemInfo> mPredictedItems = Collections.emptyList();
private AnimatorSet mIconRemoveAnimators;
- private boolean mUIUpdatePaused = false;
- private boolean mDragInProgress = false;
+ private int mPauseFlags = 0;
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
@@ -114,6 +116,30 @@
mLauncher.getDragController().addDragListener(this);
launcher.getDeviceProfile().inv.addOnChangeListener(this);
+ mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ private void onHotseatHierarchyChanged() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ // Post update after a single frame to avoid layout within layout
+ MAIN_EXECUTOR.getHandler().post(this::updateFillIfNotLoading);
+ }
+ }
+
+ private void updateFillIfNotLoading() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ fillGapsWithPrediction(true);
+ }
}
/**
@@ -160,11 +186,11 @@
}
private void fillGapsWithPrediction() {
- fillGapsWithPrediction(false, null);
+ fillGapsWithPrediction(false);
}
- private void fillGapsWithPrediction(boolean animate, Runnable callback) {
- if (mUIUpdatePaused || mDragInProgress) {
+ private void fillGapsWithPrediction(boolean animate) {
+ if (mPauseFlags != 0) {
return;
}
@@ -175,12 +201,14 @@
mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- fillGapsWithPrediction(animate, callback);
+ fillGapsWithPrediction(animate);
mIconRemoveAnimators.removeListener(this);
}
});
return;
}
+
+ mPauseFlags |= FLAG_FILL_IN_PROGRESS;
for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
View child = mHotseat.getChildAt(
mHotseat.getCellXFromOrder(rank),
@@ -207,10 +235,12 @@
}
preparePredictionInfo(predictedItem, rank);
}
- bindItems(newItems, animate, callback);
+ bindItems(newItems, animate);
+
+ mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
}
- private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
+ private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
AnimatorSet animationSet = new AnimatorSet();
for (WorkspaceItemInfo item : itemsToAdd) {
PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
@@ -221,12 +251,11 @@
}
}
if (animate) {
- if (callback != null) {
- animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
- }
+ animationSet.addListener(AnimationSuccessListener
+ .forRunnable(this::removeOutlineDrawings));
animationSet.start();
} else {
- if (callback != null) callback.run();
+ removeOutlineDrawings();
}
if (mLauncher.getTaskbarController() != null) {
@@ -234,6 +263,16 @@
}
}
+ private void removeOutlineDrawings() {
+ if (mOutlineDrawings.isEmpty()) return;
+ for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+ mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+ }
+ mHotseat.invalidate();
+ mOutlineDrawings.clear();
+ }
+
+
/**
* Unregisters callbacks and frees resources
*/
@@ -245,11 +284,9 @@
* start and pauses predicted apps update on the hotseat
*/
public void setPauseUIUpdate(boolean paused) {
- if (mLauncher.getTaskbarController() != null) {
- // Taskbar is present, always allow updates since hotseat is still visible.
- return;
- }
- mUIUpdatePaused = paused;
+ mPauseFlags = paused
+ ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+ : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
if (!paused) {
fillGapsWithPrediction();
}
@@ -365,14 +402,14 @@
for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
mHotseat.addDelegatedCellDrawing(outlineDrawing);
}
- mDragInProgress = true;
+ mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
mHotseat.invalidate();
}
@Override
public void onDragEnd() {
- mDragInProgress = false;
- fillGapsWithPrediction(true, this::removeOutlineDrawings);
+ mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+ fillGapsWithPrediction(true);
}
@Nullable
@@ -393,15 +430,6 @@
itemInfo.screenId = rank;
}
- private void removeOutlineDrawings() {
- if (mOutlineDrawings.isEmpty()) return;
- for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
- mHotseat.removeDelegatedCellDrawing(outlineDrawing);
- }
- mHotseat.invalidate();
- mOutlineDrawings.clear();
- }
-
@Override
public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
this.mHotSeatItemsCount = profile.numHotseatIcons;
@@ -416,16 +444,6 @@
* 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);
- }
-
-
ComponentName targetCN = itemInfo.getTargetComponent();
if (targetCN == null) {
return;
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index e302b4f..4d7cc85 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -17,6 +17,7 @@
package com.android.launcher3.proxy;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
@@ -48,19 +49,20 @@
return;
}
- if (mParams.intent != null) {
- startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
- return;
- } else if (mParams.intentSender != null) {
- try {
+ try {
+ if (mParams.intent != null) {
+ startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+ return;
+ } else if (mParams.intentSender != null) {
startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
mParams.extraFlags,
mParams.options);
return;
- } catch (SendIntentException e) {
- mParams.deliverResult(this, RESULT_CANCELED, null);
}
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException
+ | SendIntentException e) {
+ mParams.deliverResult(this, RESULT_CANCELED, null);
}
finishAndRemoveTask();
}
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 4a656c1..d415c36 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -121,9 +121,6 @@
case LayoutType.ICON_SINGLE_VERTICAL_TEXT:
return VIEW_TYPE_SEARCH_ICON;
case LayoutType.ICON_SLICE:
- if (FeatureFlags.DISABLE_SLICE_IN_ALLAPPS.get()) {
- return -1;
- }
if (t.getSliceUri() != null) {
return VIEW_TYPE_SEARCH_SLICE;
}
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
index 425e557..016ec1b 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
@@ -73,16 +73,15 @@
}
- private void close(boolean animate, boolean markAsSeen) {
- handleClose(animate);
- if (markAsSeen) {
- mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
- }
+ private void dismiss() {
+ handleClose(true);
+ mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
}
@Override
protected void handleClose(boolean animate) {
handleClose(animate, ANIMATION_DURATION);
+ mLauncher.getAllAppsController().getInsetController().show();
mLauncher.getStateManager().removeStateListener(this);
}
@@ -110,7 +109,7 @@
findViewById(R.id.dismiss_edu).setOnClickListener((view) -> {
mSwitchFocusOnDismiss = true;
- close(true, true);
+ dismiss();
});
}
@@ -176,7 +175,7 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
- close(true, false);
+ dismiss();
}
@Override
@@ -203,7 +202,7 @@
if (mSearchInput != null) {
mSearchInput.setText(charSequence.toString());
mSwitchFocusOnDismiss = true;
- close(true, true);
+ dismiss();
}
}
@@ -215,7 +214,7 @@
@Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
mSearchInput.onEditorAction(i);
- close(true, true);
+ dismiss();
return true;
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index f7d5f45..4c44479 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.search;
+import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_WITH_PACKAGE;
import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_PRIMARY_ICON_FROM_TITLE;
import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE;
@@ -49,7 +50,6 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.data.AppInfo;
@@ -81,7 +81,7 @@
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
private final Launcher mLauncher;
-
+ private final SearchSessionTracker mSearchSessionTracker;
private String mTargetId;
private Consumer<ItemInfoWithIcon> mOnItemInfoChanged;
@@ -97,6 +97,7 @@
public SearchResultIcon(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(getContext());
+ mSearchSessionTracker = SearchSessionTracker.getInstance(getContext());
}
private boolean mLongPressSupported;
@@ -113,8 +114,8 @@
}
/**
- * Applies {@link SearchTarget} to view. registers a consumer after a corresponding
- * {@link ItemInfoWithIcon} is created
+ * Applies {@link SearchTarget} to view. registers a consumer after a corresponding {@link
+ * ItemInfoWithIcon} is created
*/
public void apply(SearchTarget searchTarget, List<SearchTarget> inlineItems,
Consumer<ItemInfoWithIcon> cb) {
@@ -146,11 +147,10 @@
SearchActionItemInfo itemInfo = new SearchActionItemInfo(searchAction.getIcon(),
searchTarget.getPackageName(), searchTarget.getUserHandle(),
searchAction.getTitle()) {
- // Workaround to log ItemInfo with DeviceSearchResultContainer without
- // updating ItemInfo.container field.
@Override
- public ContainerInfo getContainerInfo() {
- return buildDeviceSearchResultContainer();
+ protected ExtendedContainers getExtendedContainer() {
+ return ExtendedContainers.newBuilder()
+ .setDeviceSearchResultContainer(buildDeviceSearchResultContainer()).build();
}
};
itemInfo.setIntent(searchAction.getIntent());
@@ -254,14 +254,13 @@
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
AppInfo appInfo = new AppInfo(
appsStore.getApp(new ComponentKey(componentName, userHandle))) {
- // Workaround to log ItemInfo with DeviceSearchResultContainer without
- // updating ItemInfo.container field.
@Override
- public ContainerInfo getContainerInfo() {
- return buildDeviceSearchResultContainer();
+ protected ExtendedContainers getExtendedContainer() {
+ return ExtendedContainers.newBuilder()
+ .setDeviceSearchResultContainer(buildDeviceSearchResultContainer()).build();
}
};
-
+ appInfo.container = EXTENDED_CONTAINERS;
if (appInfo == null) {
setVisibility(GONE);
return;
@@ -272,13 +271,13 @@
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext()) {
- // Workaround to log ItemInfo with DeviceSearchResultContainer without
- // updating ItemInfo.container field.
@Override
- public ContainerInfo getContainerInfo() {
- return buildDeviceSearchResultContainer();
+ protected ExtendedContainers getExtendedContainer() {
+ return ExtendedContainers.newBuilder()
+ .setDeviceSearchResultContainer(buildDeviceSearchResultContainer()).build();
}
};
+ workspaceItemInfo.container = EXTENDED_CONTAINERS;
notifyItemInfoChanged(workspaceItemInfo);
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
MODEL_EXECUTOR.execute(() -> {
@@ -317,13 +316,10 @@
}
}
- private static ContainerInfo buildDeviceSearchResultContainer() {
- return ContainerInfo.newBuilder().setExtendedContainers(
- ExtendedContainers
- .newBuilder()
- .setDeviceSearchResultContainer(
- DeviceSearchResultContainer
- .newBuilder()))
- .build();
+ private DeviceSearchResultContainer buildDeviceSearchResultContainer() {
+ return mSearchSessionTracker.getQueryLength()
+ .map(queryLength -> DeviceSearchResultContainer.newBuilder()
+ .setQueryLength(queryLength))
+ .orElse(DeviceSearchResultContainer.newBuilder()).build();
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
index c441e22..4bf3432 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
@@ -15,28 +15,22 @@
*/
package com.android.launcher3.search;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
import android.app.search.SearchTarget;
import android.app.search.SearchTargetEvent;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.widget.EventInfo;
import androidx.slice.widget.SliceView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
import java.util.List;
@@ -47,13 +41,11 @@
public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler,
SliceView.OnSliceActionListener {
- private static final String TAG = "SearchSliceController";
-
private final Launcher mLauncher;
private SliceView mSliceView;
private SearchResultIcon mIcon;
- private LiveData<Slice> mSliceLiveData;
+ private SafeCloseable mSliceSession;
private String mTargetId;
public SearchResultIconSlice(Context context) {
@@ -87,26 +79,20 @@
mTargetId = parentTarget.getId();
reset();
updateIcon(parentTarget, children);
- try {
- mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
- parentTarget.getSliceUri());
- mSliceLiveData.observe(mLauncher, mSliceView);
- } catch (Exception ex) {
- Log.e(TAG, "unable to bind slice", ex);
- }
+ mSliceSession = mLauncher.getLiveSearchManager()
+ .addObserver(parentTarget.getSliceUri(), mSliceView);
}
private void updateIcon(SearchTarget parentTarget, List<SearchTarget> children) {
if (children.size() == 1) {
mIcon.apply(children.get(0), new ArrayList<>());
} else {
- LauncherAppState appState = LauncherAppState.getInstance(getContext());
- MODEL_EXECUTOR.post(() -> {
- PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
- pkgItem.user = parentTarget.getUserHandle();
- appState.getIconCache().getTitleAndIconForApp(pkgItem, false);
- MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem));
- });
+ PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
+ pkgItem.user = parentTarget.getUserHandle();
+ if (!pkgItem.equals(mIcon.getTag())) {
+ // The icon will load and apply high res icon automatically
+ mIcon.applyFromItemInfoWithIcon(pkgItem);
+ }
}
}
@@ -124,8 +110,8 @@
private void reset() {
mSliceView.setOnSliceActionListener(null);
- if (mSliceLiveData != null) {
- mSliceLiveData.removeObservers(mLauncher);
+ if (mSliceSession != null) {
+ mSliceSession.close();
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index 4c64265..e22f6ab 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.search;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.app.search.SearchTarget;
import android.app.search.SearchTargetEvent;
import android.appwidget.AppWidgetHostView;
@@ -36,16 +39,19 @@
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.List;
-
/**
* displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
* provider
@@ -63,6 +69,7 @@
private final float mScaleToFit;
private SearchWidgetInfoContainer mInfoContainer;
+ private HandlerRunnable mLabelRequest;
private BubbleTextView mWidgetProvider;
private TextView mWidgetLabel;
@@ -124,11 +131,18 @@
}
private void showWidgetInfo(AppWidgetProviderInfo providerInfo) {
- String title = providerInfo.loadLabel(mLauncher.getPackageManager());
PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName());
pinfo.user = providerInfo.getProfile();
mWidgetProvider.applyFromItemInfoWithIcon(pinfo);
- mWidgetLabel.setText(title);
+
+ mLabelRequest = new HandlerRunnable<>(
+ MODEL_EXECUTOR.getHandler(),
+ () -> LauncherAppState.getInstance(mLauncher).getIconCache()
+ .getTitleNoCache(LauncherAppWidgetProviderInfo
+ .fromProviderInfo(mLauncher, providerInfo)),
+ MAIN_EXECUTOR,
+ mWidgetLabel::setText);
+ Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest);
}
/**
@@ -137,7 +151,18 @@
public void removeListener() {
if (mInfoContainer != null) {
mInfoContainer.detachWidget(mHostView);
+ mInfoContainer = null;
}
+ if (mLabelRequest != null) {
+ mLabelRequest.cancel();
+ mLabelRequest = null;
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ removeListener();
}
private void reportEvent(int eventType) {
diff --git a/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java b/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java
index 3079965..97a72bb 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java
@@ -15,9 +15,10 @@
*/
package com.android.launcher3.search;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.app.search.Query;
import android.app.search.SearchSession;
-import android.app.search.SearchTarget;
import android.app.search.SearchTargetEvent;
import android.content.Context;
import android.util.Log;
@@ -27,7 +28,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import java.util.Optional;
/**
* A singleton class to track and report search events back to SearchSession
@@ -63,6 +64,10 @@
mQuery = query;
}
+ public Optional<Integer> getQueryLength() {
+ return Optional.ofNullable(mQuery).map(Query::getInput).map(String::length);
+ }
+
/**
* Send the user event handling back to the {@link SearchSession} object.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
index 0093e66..3b361c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
@@ -22,10 +25,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+
/**
* Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
*/
public class TaskbarContainerView extends FrameLayout {
+
+ // Initialized in init.
+ private TaskbarView mTaskbarView;
+ private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
+
public TaskbarContainerView(@NonNull Context context) {
this(context, null);
}
@@ -43,4 +54,41 @@
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
+
+ protected void init(TaskbarView taskbarView) {
+ mTaskbarView = taskbarView;
+ mTaskbarInsetsComputer = createTaskbarInsetsComputer();
+ }
+
+ private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
+ return insetsInfo -> {
+ if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
+ // We're invisible, let touches pass through us.
+ insetsInfo.touchableRegion.setEmpty();
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ } else {
+ // We're visible again, accept touches anywhere in our bounds.
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+ }
+ };
+ }
+
+ protected void cleanup() {
+ ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
+ mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ cleanup();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 6a74aac..260428d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -38,7 +38,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.R;
-import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.states.StateAnimationConfig;
@@ -107,7 +106,6 @@
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarContainerView.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(mTaskbarContainerView);
}
};
}
@@ -167,6 +165,7 @@
public void init() {
mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
mRecentsController.getNumRecentIcons());
+ mTaskbarContainerView.init(mTaskbarView);
addToWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
mTaskbarVisibilityController.init();
@@ -188,6 +187,7 @@
*/
public void cleanup() {
mTaskbarView.cleanup();
+ mTaskbarContainerView.cleanup();
removeFromWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(null);
mTaskbarVisibilityController.cleanup();
@@ -218,6 +218,7 @@
mWindowLayoutParams.setFitInsetsTypes(0);
mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.setSystemApplicationOverlay(true);
WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
wmWrapper.setProvidesInsetsTypes(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
index 4dc051a..082343e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -57,6 +57,7 @@
protected void init() {
mLauncher.getDragController().addDragListener(mDragListener);
+ onHotseatUpdated();
}
protected void cleanup() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
index 9d4e000..4256d2b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
@@ -50,6 +50,8 @@
// The current background requests to load the task icons
private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
+ private boolean mIsAlive;
+
public TaskbarRecentsController(BaseQuickstepLauncher launcher,
TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
@@ -58,11 +60,13 @@
}
protected void init() {
+ mIsAlive = true;
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
reloadRecentTasksIfNeeded();
}
protected void cleanup() {
+ mIsAlive = false;
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mTaskStackChangeListener);
cancelAllPendingIconLoadTasks();
@@ -84,7 +88,9 @@
}
private void onRecentTasksChanged(ArrayList<Task> tasks) {
- mTaskbarCallbacks.updateRecentItems(tasks);
+ if (mIsAlive) {
+ mTaskbarCallbacks.updateRecentItems(tasks);
+ }
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 22c4a7e..2d50125 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -160,7 +160,7 @@
public boolean performAccessibilityAction(int action, ItemInfo info) {
QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext()));
if (action == PIN_PREDICTION) {
- if (launcher == null || launcher.getHotseatPredictionController() == null) {
+ if (launcher == null) {
return false;
}
HotseatPredictionController controller = launcher.getHotseatPredictionController();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ee00ed2..6098b52 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
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;
@@ -48,12 +49,12 @@
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionRowView;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.search.DeviceSearchAdapterProvider;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -82,6 +83,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
@@ -101,9 +103,7 @@
@Override
protected void setupViews() {
super.setupViews();
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- mHotseatPredictionController = new HotseatPredictionController(this);
- }
+ mHotseatPredictionController = new HotseatPredictionController(this);
}
@Override
@@ -138,9 +138,7 @@
}
logger.log(LAUNCHER_APP_LAUNCH_TAP);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
- }
+ mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
}
/**
@@ -163,9 +161,8 @@
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.setPauseUIUpdate(true);
- }
+ // Only pause is taskbar controller is not present
+ mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
return super.startActivitySafely(v, intent, item);
}
@@ -177,7 +174,7 @@
onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
}
- if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
+ if (((changeBits & ACTIVITY_STATE_STARTED) != 0
|| (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
mHotseatPredictionController.setPauseUIUpdate(false);
}
@@ -191,12 +188,8 @@
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- if (mHotseatPredictionController != null) {
- return Stream.concat(super.getSupportedShortcuts(),
- Stream.of(mHotseatPredictionController));
- } else {
- return super.getSupportedShortcuts();
- }
+ return Stream.concat(
+ super.getSupportedShortcuts(), Stream.of(mHotseatPredictionController));
}
/**
@@ -223,20 +216,25 @@
mAllAppsPredictions = item;
getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
.setPredictedApps(item.items);
- } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION
- && mHotseatPredictionController != null) {
+ } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
mHotseatPredictionController.setPredictedItems(item);
}
}
@Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+ super.bindWorkspaceItemsChanged(updated);
+ if (getTaskbarController() != null && updated.stream()
+ .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
+ getTaskbarController().onHotseatUpdated();
+ }
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
getAppsView().getSearchUiManager().destroy();
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.destroy();
- mHotseatPredictionController = null;
- }
+ mHotseatPredictionController.destroy();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 7675a79..c2e5cda 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -32,6 +32,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -150,9 +151,11 @@
mTaskBeingDragged = view;
int upDirection = mRecentsView.getPagedOrientationHandler()
.getUpDirection(mIsRtl);
- if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
+ if (!SysUINavigationMode.getMode(mActivity).hasGestures || (
+ mActivity.getDeviceProfile().isTablet
+ && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
// Don't allow swipe down to open if we don't support swipe up
- // to enter overview.
+ // to enter overview, or when grid layout is enabled.
directionsToDetectScroll = upDirection;
mAllowGoingUp = true;
mAllowGoingDown = false;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 7beeae2..8aa0842 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -184,8 +184,11 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result) {
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets);
anim.addListener(resetStateListener());
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 9da306e..25c0928 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -49,6 +49,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
@@ -174,11 +175,13 @@
final RecentsView recentsView = v.getRecentsView();
int taskIndex = recentsView.indexOfChild(v);
- boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
- int startScroll = recentsView.getScrollOffset(taskIndex);
-
Context context = v.getContext();
DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+ boolean parallaxCenterAndAdjacentTask =
+ taskIndex != recentsView.getCurrentPage() && !(dp.isTablet
+ && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
+ int startScroll = recentsView.getScrollOffset(taskIndex);
+
TaskViewSimulator topMostSimulator = null;
if (tsv == null && targets.apps.length > 0) {
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 8cde5f2..8151d41 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SYSTEM_VELOCITY_PROVIDER;
-
import android.content.Context;
import android.content.res.Resources;
import android.view.MotionEvent;
@@ -51,7 +49,7 @@
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
private final Context mContext;
- private final VelocityProvider mVelocityProvider;
+ private final SystemVelocityProvider mVelocityProvider;
private Float mPreviousVelocity = null;
@@ -88,8 +86,7 @@
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
- mVelocityProvider = ENABLE_SYSTEM_VELOCITY_PROVIDER.get()
- ? new SystemVelocityProvider(axis) : new LinearVelocityProvider(axis);
+ mVelocityProvider = new SystemVelocityProvider(axis);
}
/**
@@ -124,8 +121,8 @@
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
- Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
- if (newVelocity != null && mPreviousVelocity != null) {
+ float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+ if (mPreviousVelocity != null) {
checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
}
mPreviousVelocity = newVelocity;
@@ -210,58 +207,7 @@
default void onMotionPauseChanged(boolean isPaused) { }
}
- /**
- * Interface to abstract out velocity calculations
- */
- protected interface VelocityProvider {
-
- /**
- * Adds a new motion events, and returns the velocity at this point, or null if
- * the velocity is not available
- */
- Float addMotionEvent(MotionEvent ev, int pointer);
-
- /**
- * Clears all stored motion event records
- */
- void clear();
- }
-
- private static class LinearVelocityProvider implements VelocityProvider {
-
- private Long mPreviousTime = null;
- private Float mPreviousPosition = null;
-
- private final int mAxis;
-
- LinearVelocityProvider(int axis) {
- mAxis = axis;
- }
-
- @Override
- public Float addMotionEvent(MotionEvent ev, int pointer) {
- long time = ev.getEventTime();
- float position = ev.getAxisValue(mAxis, pointer);
- Float velocity = null;
-
- if (mPreviousTime != null && mPreviousPosition != null) {
- long changeInTime = Math.max(1, time - mPreviousTime);
- float changeInPosition = position - mPreviousPosition;
- velocity = changeInPosition / changeInTime;
- }
- mPreviousTime = time;
- mPreviousPosition = position;
- return velocity;
- }
-
- @Override
- public void clear() {
- mPreviousTime = null;
- mPreviousPosition = null;
- }
- }
-
- private static class SystemVelocityProvider implements VelocityProvider {
+ private static class SystemVelocityProvider {
private final VelocityTracker mVelocityTracker;
private final int mAxis;
@@ -271,8 +217,11 @@
mAxis = axis;
}
- @Override
- public Float addMotionEvent(MotionEvent ev, int pointer) {
+ /**
+ * Adds a new motion events, and returns the velocity at this point, or null if
+ * the velocity is not available
+ */
+ public float addMotionEvent(MotionEvent ev, int pointer) {
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1); // px / ms
return mAxis == MotionEvent.AXIS_X
@@ -280,7 +229,9 @@
: mVelocityTracker.getYVelocity(pointer);
}
- @Override
+ /**
+ * Clears all stored motion event records
+ */
public void clear() {
mVelocityTracker.clear();
}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index cb81d36..b316423 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -66,8 +66,7 @@
});
}
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
- HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {
boolean mFromAllApps = false;
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 19c6588..3adb459 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -43,8 +43,11 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonApps,
+ AnimationResult result) {
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 65bcf26..6f16781 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -276,8 +276,9 @@
int start = mOrientationState.getOrientationHandler()
.getPrimaryValue(mTaskRect.left, mTaskRect.top);
mScrollState.screenCenter = start + mScrollState.scroll + mScrollState.halfPageSize;
- mScrollState.updateInterpolation(start);
- mCurveScale = TaskView.getCurveScaleForInterpolation(mScrollState.linearInterpolation);
+ mScrollState.updateInterpolation(mDp, start);
+ mCurveScale = TaskView.getCurveScaleForInterpolation(mDp,
+ mScrollState.linearInterpolation);
}
float progress = Utilities.boundToRange(fullScreenProgress.value, 0, 1);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d9d0a93..38d488c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -898,9 +898,14 @@
public void setFullscreenProgress(float fullscreenProgress) {
mFullscreenProgress = fullscreenProgress;
int taskCount = getTaskViewCount();
+ float accumulatedTranslationX = 0;
for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+ TaskView taskView = getTaskViewAt(i);
+ taskView.setFullscreenProgress(mFullscreenProgress);
+ taskView.setAccumulatedTranslationX(accumulatedTranslationX);
+ accumulatedTranslationX += taskView.getFullscreenTranslationX();
}
+
// Fade out the actions view quickly (0.1 range)
mActionsView.getFullscreenAlpha().setValue(
mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
@@ -934,6 +939,11 @@
setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
dp.widthPx - mInsets.right - mTempRect.right,
dp.heightPx - mInsets.bottom - mTempRect.bottom);
+ // Force TaskView to update size from thumbnail
+ final int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ getTaskViewAt(i).updateTaskSize();
+ }
}
public void getTaskSize(Rect outRect) {
@@ -942,6 +952,11 @@
mLastComputedTaskSize.set(outRect);
}
+ /** Gets the last computed task size */
+ public Rect getLastComputedTaskSize() {
+ return mLastComputedTaskSize;
+ }
+
/** Gets the task size for modal state. */
public void getModalTaskSize(Rect outRect) {
mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
@@ -987,7 +1002,7 @@
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
View page = getPageAt(i);
- mScrollState.updateInterpolation(
+ mScrollState.updateInterpolation(mActivity.getDeviceProfile(),
mOrientationHandler.getChildStartWithTranslation(page));
((PageCallbacks) page).onPageScroll(mScrollState);
}
@@ -1422,13 +1437,13 @@
/**
* Updates linearInterpolation for the provided child position
*/
- public void updateInterpolation(float childStart) {
+ public void updateInterpolation(DeviceProfile deviceProfile, float childStart) {
float pageCenter = childStart + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
// How far the page has to move from the center to be offscreen, taking into account
// the EDGE_SCALE_DOWN_FACTOR that will be applied at that position.
float distanceToReachEdge = halfScreenSize
- + halfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR);
+ + halfPageSize * (1 - TaskView.getEdgeScaleDownFactor(deviceProfile));
linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
}
@@ -1444,12 +1459,13 @@
}
}
- private void addDismissedTaskAnimations(View taskView, long duration, PendingAnimation anim) {
+ private void addDismissedTaskAnimations(TaskView taskView, long duration,
+ PendingAnimation anim) {
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
- FloatProperty<View> secondaryViewTranslate =
- mOrientationHandler.getSecondaryViewTranslate();
+ FloatProperty<TaskView> secondaryViewTranslate =
+ taskView.getDismissTaskTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
@@ -1515,7 +1531,7 @@
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView
- ? ((TaskView) child).getPrimaryFillDismissGapTranslationProperty()
+ ? ((TaskView) child).getFillDismissGapTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
ResourceProvider rp = DynamicResource.provider(mActivity);
@@ -1971,7 +1987,8 @@
// Find the task's scale based on its offscreen progress, then see how far it still needs to
// move to be completely offscreen.
Utilities.scaleRectFAboutCenter(taskPosition,
- TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress));
+ TaskView.getCurveScaleForInterpolation(mActivity.getDeviceProfile(),
+ centerToOffscreenProgress));
distanceToOffscreen = desiredLeft - taskPosition.left;
// Finally, we need to account for RecentsView scale, because it moves tasks based on its
// pivot. To do this, we move the task position to where it would be offscreen at scale = 1
@@ -1990,7 +2007,7 @@
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i);
- mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
+ task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
}
@@ -2359,6 +2376,52 @@
}
@Override
+ protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
+ ComputePageScrollsLogic scrollLogic) {
+ boolean pageScrollChanged = super.getPageScrolls(outPageScrolls, layoutChildren,
+ scrollLogic);
+
+ final int taskCount = getTaskViewCount();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (childCount < mTaskViewStartIndex) {
+ continue;
+ }
+
+ final TaskView taskView = getTaskViewAt(
+ Utilities.boundToRange(i, mTaskViewStartIndex, taskCount - 1));
+ float scrollDiff =
+ taskView.getFullscreenTranslationX() + taskView.getAccumulatedTranslationX();
+ if (scrollDiff != 0) {
+ outPageScrolls[i] += scrollDiff;
+ pageScrollChanged = true;
+ }
+ }
+ return pageScrollChanged;
+ }
+
+ @Override
+ protected int getChildOffset(int index) {
+ if (index < mTaskViewStartIndex) {
+ return super.getChildOffset(index);
+ }
+
+ final TaskView taskView = getTaskViewAt(
+ Utilities.boundToRange(index, mTaskViewStartIndex, getTaskViewCount() - 1));
+ return super.getChildOffset(index) + (int) taskView.getFullscreenTranslationX()
+ + (int) taskView.getAccumulatedTranslationX();
+ }
+
+ @Override
+ protected int getChildVisibleSize(int index) {
+ final TaskView taskView = getTaskViewAtByAbsoluteIndex(index);
+ if (taskView == null) {
+ return super.getChildVisibleSize(index);
+ }
+ return super.getChildVisibleSize(index) - (int) taskView.getFullscreenTranslationX();
+ }
+
+ @Override
protected int computeMaxScroll() {
if (getTaskViewCount() > 0) {
if (mDisallowScrollToClearAll) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 445e490..3bd883d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -51,6 +51,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
@@ -110,6 +111,9 @@
private boolean mOverlayEnabled;
private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
+ // TODO(b/179466077): Remove when proper API is ready.
+ private Float mThumbnailRatio = null;
+
public TaskThumbnailView(Context context) {
this(context, null);
}
@@ -450,6 +454,31 @@
return mThumbnailData.isRealSnapshot;
}
+ // TODO(b/179466077): Remove when proper API is ready.
+ public float getThumbnailRatio() {
+ // API is ready.
+ if (mThumbnailRatio != null) {
+ return mThumbnailRatio;
+ }
+
+ if (mThumbnailData == null || mThumbnailData.thumbnail == null) {
+ final float[] thumbnailRatios =
+ new float[]{0.8882452f, 1.2834098f, 0.5558415f, 2.15625f};
+ // Use key's hash code to return a deterministic thumbnail ratio.
+ mThumbnailRatio = thumbnailRatios[mTask.key.hashCode() % thumbnailRatios.length];
+ return mThumbnailRatio;
+ }
+
+ float surfaceWidth = mThumbnailData.thumbnail.getWidth() / mThumbnailData.scale;
+ float surfaceHeight = mThumbnailData.thumbnail.getHeight() / mThumbnailData.scale;
+ float availableWidth = surfaceWidth
+ - (mThumbnailData.insets.left + mThumbnailData.insets.right);
+ float availableHeight = surfaceHeight
+ - (mThumbnailData.insets.top + mThumbnailData.insets.bottom);
+ mThumbnailRatio = availableWidth / availableHeight;
+ return mThumbnailRatio;
+ }
+
/**
* Utility class to position the thumbnail in the TaskView
*/
@@ -480,9 +509,11 @@
float scale = thumbnailData.scale;
final float thumbnailScale;
- // Landscape vs portrait change
+ // Landscape vs portrait change.
+ // Note: Disable rotation in grid layout.
boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
- && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+ && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN
+ && !(dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
isOrientationDifferent = isOrientationChange(deltaRotate)
&& windowingModeSupportsRotation;
if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index c7924fd..0654ab1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -30,6 +30,8 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
@@ -59,6 +61,7 @@
import android.view.Surface;
import android.view.TouchDelegate;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -70,6 +73,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -118,10 +122,8 @@
*/
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
- /**
- * How much to scale down pages near the edge of the screen.
- */
- public static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
+ private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
+ private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
@@ -150,29 +152,29 @@
}
};
- private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_X =
- new FloatProperty<TaskView>("fillDismissGapTranslationX") {
+ private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
+ new FloatProperty<TaskView>("dismissTranslationX") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setFillDismissGapTranslationX(v);
+ taskView.setDismissTranslationX(v);
}
@Override
public Float get(TaskView taskView) {
- return taskView.mFillDismissGapTranslationX;
+ return taskView.mDismissTranslationX;
}
};
- private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_Y =
- new FloatProperty<TaskView>("fillDismissGapTranslationY") {
+ private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
+ new FloatProperty<TaskView>("dismissTranslationY") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setFillDismissGapTranslationY(v);
+ taskView.setDismissTranslationY(v);
}
@Override
public Float get(TaskView taskView) {
- return taskView.mFillDismissGapTranslationY;
+ return taskView.mDismissTranslationY;
}
};
@@ -202,6 +204,32 @@
}
};
+ private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
+ new FloatProperty<TaskView>("taskResistanceTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskResistanceTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskResistanceTranslationX;
+ }
+ };
+
+ private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
+ new FloatProperty<TaskView>("taskResistanceTranslationY") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskResistanceTranslationY(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskResistanceTranslationY;
+ }
+ };
+
private final OnAttachStateChangeListener mTaskMenuStateListener =
new OnAttachStateChangeListener() {
@Override
@@ -226,15 +254,22 @@
private final DigitalWellBeingToast mDigitalWellBeingToast;
private float mCurveScale;
private float mFullscreenProgress;
+ private float mScaleAtFullscreen = 1;
+ private float mFullscreenScale = 1;
private final FullscreenDrawParams mCurrentFullscreenParams;
private final StatefulActivity mActivity;
// Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
- // TODO: We should do this for secondary translation properties as well.
- private float mFillDismissGapTranslationX;
- private float mFillDismissGapTranslationY;
+ private float mDismissTranslationX;
+ private float mDismissTranslationY;
private float mTaskOffsetTranslationX;
private float mTaskOffsetTranslationY;
+ private float mTaskResistanceTranslationX;
+ private float mTaskResistanceTranslationY;
+ // The following translation variables should only be used in the same orientation as Launcher.
+ private float mFullscreenTranslationX;
+ private float mAccumulatedTranslationX;
+ private float mBoxTranslationY;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -420,6 +455,7 @@
cancelPendingLoadTasks();
mTask = task;
mSnapshotView.bind(task);
+ updateTaskSize();
setOrientationState(orientedState);
}
@@ -663,10 +699,12 @@
protected void resetViewTransforms() {
setCurveScale(1);
- mFillDismissGapTranslationX = mTaskOffsetTranslationX = 0f;
- mFillDismissGapTranslationY = mTaskOffsetTranslationY = 0f;
- setTranslationX(0f);
- setTranslationY(0f);
+ // fullscreenTranslation and accumulatedTranslation should not be reset, as
+ // resetViewTransforms is called during Quickswitch scrolling.
+ mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f;
+ mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+ applyTranslationX();
+ applyTranslationY();
setTranslationZ(0);
setAlpha(mStableAlpha);
setIconScaleAndDim(1);
@@ -679,6 +717,7 @@
@Override
public void onRecycle() {
+ mFullscreenTranslationX = mAccumulatedTranslationX = mBoxTranslationY = 0f;
resetViewTransforms();
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
@@ -697,7 +736,7 @@
float curveInterpolation =
CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
- curveInterpolation);
+ mActivity.getDeviceProfile(), curveInterpolation);
mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
setCurveScale(curveScaleForCurveInterpolation);
@@ -768,40 +807,70 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- setPivotX((right - left) * 0.5f);
- setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? (right - left) : 0);
+ setPivotY(0);
+ } else {
+ setPivotX((right - left) * 0.5f);
+ setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+ }
if (Utilities.ATLEAST_Q) {
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
}
- public static float getCurveScaleForInterpolation(float linearInterpolation) {
+ /**
+ * How much to scale down pages near the edge of the screen according to linearInterpolation.
+ */
+ public static float getCurveScaleForInterpolation(DeviceProfile deviceProfile,
+ float linearInterpolation) {
float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
- return getCurveScaleForCurveInterpolation(curveInterpolation);
+ return getCurveScaleForCurveInterpolation(deviceProfile, curveInterpolation);
}
- private static float getCurveScaleForCurveInterpolation(float curveInterpolation) {
- return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+ private static float getCurveScaleForCurveInterpolation(DeviceProfile deviceProfile,
+ float curveInterpolation) {
+ return 1 - curveInterpolation * getEdgeScaleDownFactor(deviceProfile);
+ }
+
+ /**
+ * How much to scale down pages near the edge of the screen.
+ */
+ public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
+ if (deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ return EDGE_SCALE_DOWN_FACTOR_GRID;
+ } else {
+ return EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
+ }
}
private void setCurveScale(float curveScale) {
mCurveScale = curveScale;
- setScaleX(mCurveScale);
- setScaleY(mCurveScale);
+ applyScale();
+ }
+
+ private void setFullscreenScale(float fullscreenScale) {
+ mFullscreenScale = fullscreenScale;
+ applyScale();
+ }
+
+ private void applyScale() {
+ setScaleX(mCurveScale * mFullscreenScale);
+ setScaleY(mCurveScale * mFullscreenScale);
}
public float getCurveScale() {
return mCurveScale;
}
- private void setFillDismissGapTranslationX(float x) {
- mFillDismissGapTranslationX = x;
+ private void setDismissTranslationX(float x) {
+ mDismissTranslationX = x;
applyTranslationX();
}
- private void setFillDismissGapTranslationY(float y) {
- mFillDismissGapTranslationY = y;
+ private void setDismissTranslationY(float y) {
+ mDismissTranslationY = y;
applyTranslationY();
}
@@ -815,17 +884,59 @@
applyTranslationY();
}
+ private void setTaskResistanceTranslationX(float x) {
+ mTaskResistanceTranslationX = x;
+ applyTranslationX();
+ }
+
+ private void setTaskResistanceTranslationY(float y) {
+ mTaskResistanceTranslationY = y;
+ applyTranslationY();
+ }
+
+ private void setFullscreenTranslationX(float fullscreenTranslationX) {
+ mFullscreenTranslationX = fullscreenTranslationX;
+ applyTranslationX();
+ }
+
+ public float getFullscreenTranslationX() {
+ return mFullscreenTranslationX;
+ }
+
+ public void setAccumulatedTranslationX(float accumulatedTranslationX) {
+ mAccumulatedTranslationX = accumulatedTranslationX;
+ applyTranslationX();
+ }
+
+ public float getAccumulatedTranslationX() {
+ return mAccumulatedTranslationX;
+ }
+
+ private void setBoxTranslationY(float boxTranslationY) {
+ mBoxTranslationY = boxTranslationY;
+ applyTranslationY();
+ }
+
private void applyTranslationX() {
- setTranslationX(mFillDismissGapTranslationX + mTaskOffsetTranslationX);
+ setTranslationX(
+ mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
+ + mFullscreenTranslationX + mAccumulatedTranslationX);
}
private void applyTranslationY() {
- setTranslationY(mFillDismissGapTranslationY + mTaskOffsetTranslationY);
+ setTranslationY(
+ mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
+ + mBoxTranslationY);
}
- public FloatProperty<TaskView> getPrimaryFillDismissGapTranslationProperty() {
+ public FloatProperty<TaskView> getFillDismissGapTranslationProperty() {
return getPagedOrientationHandler().getPrimaryValue(
- FILL_DISMISS_GAP_TRANSLATION_X, FILL_DISMISS_GAP_TRANSLATION_Y);
+ DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
+ }
+
+ public FloatProperty<TaskView> getDismissTaskTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
@@ -833,6 +944,11 @@
TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
}
+ public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
+ }
+
@Override
public boolean hasOverlappingRendering() {
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
@@ -965,6 +1081,8 @@
mFullscreenProgress = progress;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
+ updateTaskScaling();
+
TaskThumbnailView thumbnail = getThumbnail();
updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
@@ -990,6 +1108,83 @@
previewPositionHelper);
}
+ void updateTaskSize() {
+ ViewGroup.LayoutParams params = getLayoutParams();
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ final int thumbnailPadding = (int) getResources().getDimension(
+ R.dimen.task_thumbnail_top_margin);
+
+ Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
+ int taskWidth = lastComputedTaskSize.width();
+ int taskHeight = lastComputedTaskSize.height();
+ int boxLength = Math.max(taskWidth, taskHeight);
+ float thumbnailRatio = mSnapshotView.getThumbnailRatio();
+
+ int expectedWidth;
+ int expectedHeight;
+ if (thumbnailRatio > 1) {
+ expectedWidth = boxLength;
+ expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding;
+ } else {
+ expectedWidth = (int) (boxLength * thumbnailRatio);
+ expectedHeight = boxLength + thumbnailPadding;
+ }
+
+ float heightDiff = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
+ setBoxTranslationY(heightDiff);
+
+ if (expectedWidth > taskWidth) {
+ // In full screen, expectedWidth should not be larger than taskWidth.
+ mScaleAtFullscreen = taskWidth / (float) expectedWidth;
+ } else if (expectedHeight - thumbnailPadding > taskHeight) {
+ // In full screen, expectedHeight should not be larger than taskHeight.
+ mScaleAtFullscreen = taskHeight / (float) (expectedHeight - thumbnailPadding);
+ } else {
+ mScaleAtFullscreen = 1f;
+ }
+
+ if (params.width != expectedWidth || params.height != expectedHeight) {
+ params.width = expectedWidth;
+ params.height = expectedHeight;
+ setLayoutParams(params);
+ }
+ } else {
+ setBoxTranslationY(0);
+ if (params.width != ViewGroup.LayoutParams.MATCH_PARENT) {
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ setLayoutParams(params);
+ }
+ }
+ updateTaskScaling();
+ }
+
+ private void updateTaskScaling() {
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ ViewGroup.LayoutParams params = getLayoutParams();
+ if (params.width == ViewGroup.LayoutParams.MATCH_PARENT
+ || params.height == ViewGroup.LayoutParams.MATCH_PARENT) {
+ // Snapshot is not loaded yet, skip.
+ return;
+ }
+
+ float progress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress);
+ setFullscreenScale(Utilities.mapRange(progress, 1f, mScaleAtFullscreen));
+
+ float widthDiff = params.width * (1 - mFullscreenScale);
+ setFullscreenTranslationX(getFullscreenTrans(
+ getLayoutDirection() == LAYOUT_DIRECTION_RTL ? -widthDiff : widthDiff));
+ } else {
+ setFullscreenScale(1);
+ setFullscreenTranslationX(0);
+ }
+ }
+
+ private float getFullscreenTrans(float endTranslation) {
+ float progress = ACCEL_DEACCEL.getInterpolation(mFullscreenProgress);
+ return Utilities.mapRange(progress, 0, endTranslation);
+ }
+
public boolean isRunningTask() {
if (getRecentsView() == null) {
return false;
diff --git a/res/layout/keyboard_drag_and_drop.xml b/res/layout/keyboard_drag_and_drop.xml
new file mode 100644
index 0000000..e9463c4
--- /dev/null
+++ b/res/layout/keyboard_drag_and_drop.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<com.android.launcher3.keyboard.KeyboardDragAndDropView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:orientation="vertical"
+ android:elevation="6dp">
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="?attr/folderFillColor"
+ android:padding="8dp"
+ android:textColor="?attr/folderTextColor"
+ />
+
+</com.android.launcher3.keyboard.KeyboardDragAndDropView>
\ No newline at end of file
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..b8600a6
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2021, 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.
+*/
+-->
+<resources>
+ <color name="popup_color_primary_light">@android:color/system_main_50</color>
+ <color name="popup_color_secondary_light">@android:color/system_main_100</color>
+ <color name="popup_color_tertiary_light">@android:color/system_main_300</color>
+ <color name="popup_color_primary_dark">@android:color/system_main_800</color>
+ <color name="popup_color_secondary_dark">@android:color/system_main_900</color>
+ <color name="popup_color_tertiary_dark">@android:color/system_main_700</color>
+
+ <color name="workspace_text_color_light">@android:color/system_main_50</color>
+ <color name="workspace_text_color_dark">@android:color/system_main_900</color>
+
+ <color name="text_color_primary_dark">@android:color/system_main_50</color>
+ <color name="text_color_secondary_dark">@android:color/system_main_200</color>
+ <color name="text_color_tertiary_dark">@android:color/system_main_400</color>
+</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 78c2df6..0b30253 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -40,4 +40,19 @@
<color name="gesture_tutorial_fake_previous_task_view_color">#9CCC65</color> <!-- Light Green -->
<color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
<color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
+
+ <color name="popup_color_primary_light">#FFF</color>
+ <color name="popup_color_secondary_light">#F1F3F4</color>
+ <color name="popup_color_tertiary_light">#E0E0E0</color> <!-- Gray 300 -->
+ <color name="popup_color_primary_dark">#3C4043</color> <!-- Gray 800 -->
+ <color name="popup_color_secondary_dark">#202124</color>
+ <color name="popup_color_tertiary_dark">#757575</color> <!-- Gray 600 -->
+
+ <color name="workspace_text_color_light">#FFF</color>
+ <color name="workspace_text_color_dark">#FF212121</color>
+
+ <color name="text_color_primary_dark">#FFFFFFFF</color>
+ <color name="text_color_secondary_dark">#FFFFFFFF</color>
+ <color name="text_color_tertiary_dark">#CCFFFFFF</color>
+
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index cf51f77..eaf7a5f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -139,6 +139,7 @@
<dimen name="drag_flingToDeleteMinVelocity">-1500dp</dimen>
<dimen name="spring_loaded_panel_border">1dp</dimen>
+ <dimen name="keyboard_drag_stroke_width">4dp</dimen>
<!-- Folders -->
<dimen name="page_indicator_dot_size">8dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dc7182f..adc2238 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,12 +35,12 @@
<item name="allAppsInterimScrimAlpha">46</item>
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
<item name="allAppsTheme">@style/AllAppsTheme</item>
- <item name="popupColorPrimary">#FFF</item>
- <item name="popupColorSecondary">#F1F3F4</item>
- <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+ <item name="popupColorPrimary">@color/popup_color_primary_light</item>
+ <item name="popupColorSecondary">@color/popup_color_secondary_light</item>
+ <item name="popupColorTertiary">@color/popup_color_tertiary_light</item>
<item name="isMainColorDark">false</item>
<item name="isWorkspaceDarkText">false</item>
- <item name="workspaceTextColor">@android:color/white</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_light</item>
<item name="workspaceShadowColor">#B0000000</item>
<item name="workspaceAmbientShadowColor">#33000000</item>
<item name="workspaceKeyShadowColor">#44000000</item>
@@ -74,7 +74,7 @@
</style>
<style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
- <item name="workspaceTextColor">#FF212121</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
<item name="allAppsInterimScrimAlpha">128</item>
<item name="workspaceShadowColor">@android:color/transparent</item>
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -88,9 +88,9 @@
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
- <item name="android:textColorPrimary">#FFFFFFFF</item>
- <item name="android:textColorSecondary">#FFFFFFFF</item>
- <item name="android:textColorTertiary">#CCFFFFFF</item>
+ <item name="android:textColorPrimary">@color/text_color_primary_dark</item>
+ <item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
+ <item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
<item name="android:textColorHint">#A0FFFFFF</item>
<item name="android:colorControlHighlight">#A0FFFFFF</item>
<item name="android:colorPrimary">#FF212121</item>
@@ -98,9 +98,9 @@
<item name="allAppsInterimScrimAlpha">102</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
- <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
- <item name="popupColorSecondary">#202124</item>
- <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+ <item name="popupColorPrimary">@color/popup_color_primary_dark</item>
+ <item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
+ <item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="folderDotColor">?android:attr/colorPrimary</item>
<item name="folderFillColor">?android:attr/colorBackground</item>
@@ -125,7 +125,7 @@
<item name="allAppsInterimScrimAlpha">25</item>
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
- <item name="workspaceTextColor">#FF212121</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
<item name="workspaceShadowColor">@android:color/transparent</item>
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
<item name="workspaceKeyShadowColor">@android:color/transparent</item>
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index 4e811f3..0ac997f 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1,4 +1,4 @@
-sdk=29
+sdk=30
shadows= \
com.android.launcher3.shadows.LShadowAppPredictionManager \
com.android.launcher3.shadows.LShadowAppWidgetManager \
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 3c34444..6037c96 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -76,17 +76,18 @@
public static final int TYPE_SNACKBAR = 1 << 7;
public static final int TYPE_LISTENER = 1 << 8;
public static final int TYPE_ALL_APPS_EDU = 1 << 9;
+ public static final int TYPE_DRAG_DROP_POPUP = 1 << 10;
// Popups related to quickstep UI
- public static final int TYPE_TASK_MENU = 1 << 10;
- public static final int TYPE_OPTIONS_POPUP = 1 << 11;
- public static final int TYPE_ICON_SURFACE = 1 << 12;
+ public static final int TYPE_TASK_MENU = 1 << 11;
+ public static final int TYPE_OPTIONS_POPUP = 1 << 12;
+ public static final int TYPE_ICON_SURFACE = 1 << 13;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
- | TYPE_ICON_SURFACE;
+ | TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
@@ -103,7 +104,7 @@
// These view all have particular operation associated with swipe down interaction.
public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_ON_BOARD_POPUP |
- TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU ;
+ TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU | TYPE_DRAG_DROP_POPUP;
protected boolean mIsOpen;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6f446ad..21297c9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -66,9 +66,9 @@
import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -171,7 +171,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
- private IconLoadRequest mIconLoadRequest;
+ private HandlerRunnable mIconLoadRequest;
private boolean mEnableIconUpdateAnimation = false;
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index df005e6..459b9a8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -240,7 +240,7 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- mActive = supportsDrop(dragObject.dragInfo);
+ mActive = !options.isKeyboardDrag && supportsDrop(dragObject.dragInfo);
mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 2809bd5..452207d 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -312,6 +312,13 @@
}
}
+ /**
+ * Returns the currently set accessibility delegate
+ */
+ public DragAndDropAccessibilityDelegate getDragAndDropAccessibilityDelegate() {
+ return mTouchHelper;
+ }
+
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// Always attempt to dispatch hover events to accessibility first.
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index ca001a3..c768493 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -131,7 +131,10 @@
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
- if (mIsVertical) {
+ int visibleCount = getVisibleButtonsCount();
+ if (visibleCount == 0) {
+ // do nothing
+ } else if (mIsVertical) {
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
@@ -142,7 +145,6 @@
}
}
} else {
- int visibleCount = getVisibleButtonsCount();
int availableWidth = width / visibleCount;
boolean textVisible = true;
for (ButtonDropTarget buttons : mDropTargets) {
@@ -165,7 +167,10 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mIsVertical) {
+ int visibleCount = getVisibleButtonsCount();
+ if (visibleCount == 0) {
+ // do nothing
+ } else if (mIsVertical) {
int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
int start = gap;
int end;
@@ -178,7 +183,6 @@
}
}
} else {
- int visibleCount = getVisibleButtonsCount();
int frameSize = (right - left) / visibleCount;
int start = frameSize / 2;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5fd9e01..aa3ef9b 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString;
-import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
@@ -174,8 +173,7 @@
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
.apply();
- mConfigMonitor = new ConfigMonitor(context,
- APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+ mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
mOverlayMonitor = new OverlayMonitor(context);
}
@@ -317,11 +315,6 @@
mChangeListeners.remove(listener);
}
- private void killProcess(Context context) {
- Log.e("ConfigMonitor", "restarting launcher");
- android.os.Process.killProcess(android.os.Process.myPid());
- }
-
public void verifyConfigChangedInBackground(final Context context) {
String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
// Good place to check if grid size changed in themepicker when launcher was dead.
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2334267..344ae0a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -98,13 +98,9 @@
import android.widget.Toast;
import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -211,8 +207,7 @@
* Default launcher application.
*/
public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
- Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
- LifecycleOwner {
+ Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -275,8 +270,6 @@
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
- private LifecycleRegistry mLifecycleRegistry;
-
private LiveSearchManager mLiveSearchManager;
@Thunk
@@ -392,12 +385,12 @@
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
- mLiveSearchManager = new LiveSearchManager(this);
-
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
+ mLiveSearchManager = new LiveSearchManager(this);
+
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
mAppWidgetManager = new WidgetManagerHelper(this);
@@ -486,15 +479,6 @@
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
-
- mLifecycleRegistry = new LifecycleRegistry(this);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycleRegistry;
}
public LiveSearchManager getLiveSearchManager() {
@@ -913,7 +897,6 @@
@Override
protected void onStop() {
- mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
super.onStop();
if (mDeferOverlayCallbacks) {
checkIfOverlayStillDeferred();
@@ -922,7 +905,7 @@
}
logStopAndResume(false /* isResume */);
- mAppWidgetHost.setListenIfResumed(false);
+ mAppWidgetHost.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener();
}
@@ -935,9 +918,8 @@
mOverlayManager.onActivityStarted(this);
}
- mAppWidgetHost.setListenIfResumed(true);
+ mAppWidgetHost.setActivityStarted(true);
TraceHelper.INSTANCE.endSection(traceToken);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
}
@Override
@@ -956,6 +938,7 @@
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
DiscoveryBounce.showForHomeIfNeeded(this);
+ mAppWidgetHost.setActivityResumed(true);
}
private void logStopAndResume(boolean isResume) {
@@ -1049,7 +1032,7 @@
@Override
public void onStateSetEnd(LauncherState state) {
super.onStateSetEnd(state);
- getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
finishAutoCancelActionMode();
@@ -1091,7 +1074,6 @@
}
TraceHelper.INSTANCE.endSection(traceToken);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
}
@Override
@@ -1099,7 +1081,6 @@
// Ensure that items added to Launcher are queued until Launcher returns
ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
super.onPause();
mDragController.cancelDrag();
mLastTouchUpTime = -1;
@@ -1108,6 +1089,7 @@
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityPaused(this);
}
+ mAppWidgetHost.setActivityResumed(false);
}
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1598,7 +1580,6 @@
mAppTransitionManager.unregisterRemoteAnimations();
mAppTransitionManager.unregisterRemoteTransitions();
mUserChangedCallbackCloseable.close();
- mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
mLiveSearchManager.stop();
}
@@ -2688,7 +2669,8 @@
&& focusedView.getTag() instanceof ItemInfo
&& mAccessibilityDelegate.performAction(focusedView,
(ItemInfo) focusedView.getTag(),
- LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
+ LauncherAccessibilityDelegate.DEEP_SHORTCUTS,
+ true)) {
PopupContainerWithArrow.getOpen(this).requestFocus();
return true;
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7ea6851..fea26df 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -49,8 +49,11 @@
public class LauncherAppWidgetHost extends AppWidgetHost {
private static final int FLAG_LISTENING = 1;
- private static final int FLAG_RESUMED = 1 << 1;
- private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2;
+ private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
+ private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
+ private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+ private static final int FLAGS_SHOULD_LISTEN =
+ FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
public static final int APPWIDGET_HOST_ID = 1024;
@@ -59,7 +62,7 @@
private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
private final Context mContext;
- private int mFlags = FLAG_RESUMED;
+ private int mFlags = FLAG_STATE_IS_NORMAL;
private IntConsumer mAppWidgetRemovedCallback = null;
@@ -130,49 +133,45 @@
}
/**
- * Updates the resumed state of the host.
- * When a host is not resumed, it defers calls to startListening until host is resumed again.
- * But if the host was already listening, it will not call stopListening.
- *
- * @see #setListenIfResumed(boolean)
+ * Sets or unsets a flag the can change whether the widget host should be in the listening
+ * state.
*/
- public void setResumed(boolean isResumed) {
- if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) {
- return;
- }
- if (isResumed) {
- mFlags |= FLAG_RESUMED;
- // Start listening if we were supposed to start listening on resume
- if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) {
- startListening();
- }
+ private void setShouldListenFlag(int flag, boolean on) {
+ if (on) {
+ mFlags |= flag;
} else {
- mFlags &= ~FLAG_RESUMED;
+ mFlags &= ~flag;
+ }
+
+ final boolean listening = isListening();
+ if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
+ // Postpone starting listening until all flags are on.
+ startListening();
+ } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
+ // Postpone stopping listening until the activity is stopped.
+ stopListening();
}
}
/**
- * Updates the listening state of the host. If the host is not resumed, startListening is
- * deferred until next resume.
- *
- * @see #setResumed(boolean)
+ * Registers an "entering/leaving Normal state" event.
*/
- public void setListenIfResumed(boolean listenIfResumed) {
- if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
- return;
- }
- if (listenIfResumed) {
- mFlags |= FLAG_LISTEN_IF_RESUMED;
- if ((mFlags & FLAG_RESUMED) != 0) {
- // If we are resumed, start listening immediately. Note we do not check for
- // duplicate calls before calling startListening as startListening is safe to call
- // multiple times.
- startListening();
- }
- } else {
- mFlags &= ~FLAG_LISTEN_IF_RESUMED;
- stopListening();
- }
+ public void setStateIsNormal(boolean isNormal) {
+ setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
+ }
+
+ /**
+ * Registers an "activity started/stopped" event.
+ */
+ public void setActivityStarted(boolean isStarted) {
+ setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
+ }
+
+ /**
+ * Registers an "activity paused/resumed" event.
+ */
+ public void setActivityResumed(boolean isResumed) {
+ setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
}
@Override
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index fe423ed..be270397 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -171,6 +171,9 @@
public static final int CONTAINER_SETTINGS = -108;
public static final int CONTAINER_TASKSWITCHER = -109;
+ // Represents any of the extended containers implemented in non-AOSP variants.
+ public static final int EXTENDED_CONTAINERS = -200;
+
public static final String containerToString(int container) {
switch (container) {
case CONTAINER_DESKTOP: return "desktop";
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 4303dee..ada297f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -745,6 +745,11 @@
return mOrientationHandler.getChildStart(pageAtIndex);
}
+ protected int getChildVisibleSize(int index) {
+ View layout = getPageAt(index);
+ return mOrientationHandler.getMeasuredSize(layout);
+ }
+
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int page = indexToPage(indexOfChild(child));
@@ -1457,8 +1462,7 @@
}
private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) {
- View layout = getPageAt(childIndex);
- int childSize = mOrientationHandler.getMeasuredSize(layout);
+ int childSize = getChildVisibleSize(childIndex);
int halfChildSize = (childSize / 2);
int childCenter = getChildOffset(childIndex) + halfChildSize;
return childCenter - screenCenter;
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index ddb547f..d0fc175 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -31,6 +31,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragLayer;
import java.util.List;
@@ -41,30 +42,32 @@
implements OnClickListener, OnHoverListener {
protected static final int INVALID_POSITION = -1;
- private static final int[] sTempArray = new int[2];
+ protected final Rect mTempRect = new Rect();
+ protected final int[] mTempCords = new int[2];
protected final CellLayout mView;
protected final Context mContext;
protected final LauncherAccessibilityDelegate mDelegate;
-
- private final Rect mTempRect = new Rect();
+ protected final DragLayer mDragLayer;
public DragAndDropAccessibilityDelegate(CellLayout forView) {
super(forView);
mView = forView;
mContext = mView.getContext();
- mDelegate = Launcher.getLauncher(mContext).getAccessibilityDelegate();
+ Launcher launcher = Launcher.getLauncher(mContext);
+ mDelegate = launcher.getAccessibilityDelegate();
+ mDragLayer = launcher.getDragLayer();
}
@Override
- protected int getVirtualViewAt(float x, float y) {
+ public int getVirtualViewAt(float x, float y) {
if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) {
return INVALID_ID;
}
- mView.pointToCellExact((int) x, (int) y, sTempArray);
+ mView.pointToCellExact((int) x, (int) y, mTempCords);
// Map cell to id
- int id = sTempArray[0] + sTempArray[1] * mView.getCountX();
+ int id = mTempCords[0] + mTempCords[1] * mView.getCountX();
return intersectsValidDropTarget(id);
}
@@ -75,7 +78,7 @@
protected abstract int intersectsValidDropTarget(int id);
@Override
- protected void getVisibleVirtualViews(List<Integer> virtualViews) {
+ public void getVisibleVirtualViews(List<Integer> virtualViews) {
// We create a virtual view for each cell of the grid
// The cell ids correspond to cells in reading order.
int nCells = mView.getCountX() * mView.getCountY();
@@ -88,7 +91,7 @@
}
@Override
- protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
+ public boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) {
String confirmation = getConfirmationForIconDrop(viewId);
mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation);
@@ -112,13 +115,25 @@
}
@Override
- protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
+ public void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
if (id == INVALID_ID) {
throw new IllegalArgumentException("Invalid virtual view id");
}
node.setContentDescription(getLocationDescriptionForIconDrop(id));
- node.setBoundsInParent(getItemBounds(id));
+
+ Rect itemBounds = getItemBounds(id);
+ node.setBoundsInParent(itemBounds);
+
+ // ExploreByTouchHelper does not currently handle view scale.
+ // Update BoundsInScreen to appropriate value.
+ mTempCords[0] = mTempCords[1] = 0;
+ float scale = mDragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
+ mTempRect.left = mTempCords[0] + (int) (itemBounds.left * scale);
+ mTempRect.right = mTempCords[0] + (int) (itemBounds.right * scale);
+ mTempRect.top = mTempCords[1] + (int) (itemBounds.top * scale);
+ mTempRect.bottom = mTempCords[1] + (int) (itemBounds.bottom * scale);
+ node.setBoundsInScreen(mTempRect);
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
node.setClickable(true);
@@ -130,6 +145,13 @@
return dispatchHoverEvent(motionEvent);
}
+ /**
+ * Returns the target host container
+ */
+ public View getHost() {
+ return mView;
+ }
+
protected abstract String getLocationDescriptionForIconDrop(int id);
protected abstract String getConfirmationForIconDrop(int id);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 136d43e..6fac79a 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -34,6 +34,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.keyboard.CustomActionsPopup;
+import com.android.launcher3.keyboard.KeyboardDragAndDropView;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -107,10 +108,6 @@
launcher.getText(R.string.shortcuts_menu_with_notifications_description)));
}
- public void addAccessibilityAction(int action, int actionLabel) {
- mActions.put(action, new AccessibilityAction(action, mLauncher.getText(actionLabel)));
- }
-
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
@@ -139,7 +136,7 @@
}
// Do not add move actions for keyboard request as this uses virtual nodes.
- if (!fromKeyboard && itemSupportsAccessibleDrag(item)) {
+ if (itemSupportsAccessibleDrag(item)) {
info.addAction(mActions.get(MOVE));
if (item.container >= 0) {
@@ -178,13 +175,17 @@
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if ((host.getTag() instanceof ItemInfo)
- && performAction(host, (ItemInfo) host.getTag(), action)) {
+ && performAction(host, (ItemInfo) host.getTag(), action, false)) {
return true;
}
return super.performAccessibilityAction(host, action, args);
}
- public boolean performAction(final View host, final ItemInfo item, int action) {
+ /**
+ * Performs the provided action on the host
+ */
+ public boolean performAction(final View host, final ItemInfo item, int action,
+ boolean fromKeyboard) {
if (action == ACTION_LONG_CLICK) {
if (PopupContainerWithArrow.canShow(host, item)) {
// Long press should be consumed for workspace items, and it should invoke the
@@ -205,7 +206,7 @@
return true;
}
if (action == MOVE) {
- beginAccessibleDrag(host, item);
+ return beginAccessibleDrag(host, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
@@ -406,7 +407,11 @@
}
}
- public void beginAccessibleDrag(View item, ItemInfo info) {
+ private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
+ if (!itemSupportsAccessibleDrag(info)) {
+ return false;
+ }
+
mDragInfo = new DragInfo();
mDragInfo.info = info;
mDragInfo.item = item;
@@ -423,8 +428,17 @@
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
+ options.isKeyboardDrag = fromKeyboard;
options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
- ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+
+ if (fromKeyboard) {
+ KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
+ .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
+ popup.showForIcon(item, info, options);
+ } else {
+ ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+ }
+ return true;
}
@Override
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index d4ba11e..aaaff98 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -59,7 +59,7 @@
}
@Override
- public boolean performAction(View host, ItemInfo item, int action) {
+ public boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
if (action == ADD_TO_WORKSPACE) {
if (!(host.getParent() instanceof DeepShortcutView)) {
return false;
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 65a261d..a331924 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -17,17 +17,12 @@
package com.android.launcher3.accessibility;
import android.content.Context;
-import android.graphics.Rect;
import android.text.TextUtils;
import android.view.View;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -38,9 +33,6 @@
*/
public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
- private final Rect mTempRect = new Rect();
- private final int[] mTempCords = new int[2];
-
public WorkspaceAccessibilityHelper(CellLayout layout) {
super(layout);
}
@@ -134,26 +126,6 @@
}
return "";
}
-
- @Override
- protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
- super.onPopulateNodeForVirtualView(id, node);
-
-
- // ExploreByTouchHelper does not currently handle view scale.
- // Update BoundsInScreen to appropriate value.
- DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
- mTempCords[0] = mTempCords[1] = 0;
- float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
-
- node.getBoundsInParent(mTempRect);
- mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
- mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
- mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
- mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
- node.setBoundsInScreen(mTempRect);
- }
-
@Override
protected String getLocationDescriptionForIconDrop(int id) {
int x = id % mView.getCountX();
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
index f6e54aa..b34c8b8 100644
--- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -86,6 +86,10 @@
mApps = appsView;
}
+ public void show() {
+ mApps.getWindowInsetsController().show(WindowInsets.Type.ime());
+ }
+
public void hide() {
if (!Utilities.ATLEAST_R) return;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2f805fd..a4e1f27 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -268,13 +268,10 @@
&& !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) {
mInsetController.onAnimationEnd(mProgress);
if (Float.compare(mProgress, 0f) == 0) {
- mLauncher.getLiveSearchManager().start();
EditText editText = mAppsView.getSearchUiManager().getEditText();
if (editText != null && !mInsetController.showSearchEduIfNecessary()) {
editText.requestFocus();
}
- } else {
- mLauncher.getLiveSearchManager().stop();
}
// TODO: should make the controller hide synchronously
}
diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
index c2f0b96..e52c790 100644
--- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java
+++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
@@ -15,8 +15,16 @@
*/
package com.android.launcher3.allapps.search;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -26,39 +34,52 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.Log;
-import androidx.lifecycle.LiveData;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Observer;
import androidx.slice.Slice;
-import androidx.slice.widget.SliceLiveData;
+import androidx.slice.SliceViewManager;
+import androidx.slice.SliceViewManager.SliceCallback;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Optional;
/**
* Manages Lifecycle for Live search results
*/
-public class LiveSearchManager {
+public class LiveSearchManager implements StateListener<LauncherState> {
+
+ private static final String TAG = "LiveSearchManager";
public static final int SEARCH_APPWIDGET_HOST_ID = 2048;
private final Launcher mLauncher;
- private final AppWidgetManager mAppWidgetManger;
+ private final HashMap<Uri, SliceLifeCycle> mUriSliceMap = new HashMap<>();
+
private final HashMap<ComponentKey, SearchWidgetInfoContainer> mWidgetPlaceholders =
new HashMap<>();
- private final HashMap<Uri, LiveData<Slice>> mUriSliceMap = new HashMap<>();
private SearchWidgetHost mSearchWidgetHost;
private InstanceId mLogInstanceId;
+ private LauncherState mPrevLauncherState;
public LiveSearchManager(Launcher launcher) {
mLauncher = launcher;
- mAppWidgetManger = AppWidgetManager.getInstance(launcher);
+ mLauncher.getStateManager().addStateListener(this);
}
/**
@@ -67,78 +88,94 @@
*/
public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) {
if (mSearchWidgetHost == null) {
- throw new RuntimeException("AppWidgetHost has not been created yet");
+ mSearchWidgetHost = new SearchWidgetHost(mLauncher);
+ mSearchWidgetHost.startListening();
}
ComponentName provider = providerInfo.provider;
UserHandle userHandle = providerInfo.getProfile();
ComponentKey key = new ComponentKey(provider, userHandle);
- SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null);
if (mWidgetPlaceholders.containsKey(key)) {
return mWidgetPlaceholders.get(key);
}
+
LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
mLauncher, providerInfo);
PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo);
Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo);
int appWidgetId = mSearchWidgetHost.allocateAppWidgetId();
- boolean success = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle,
- provider, options);
+ boolean success = AppWidgetManager.getInstance(mLauncher)
+ .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options);
if (!success) {
+ mSearchWidgetHost.deleteAppWidgetId(appWidgetId);
mWidgetPlaceholders.put(key, null);
return null;
}
- view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId,
- providerInfo);
+ SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(
+ mLauncher, appWidgetId, providerInfo);
view.setTag(pendingAddWidgetInfo);
mWidgetPlaceholders.put(key, view);
return view;
}
/**
- * Creates {@link LiveData<Slice>} from Slice Uri. Caches created live data to be reused
- * within the same search session. Removes previous observers when new SliceView request a
- * live data for observation.
- */
- public LiveData<Slice> getSliceForUri(Uri sliceUri) {
- LiveData<Slice> sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null);
- if (sliceLiveData == null) {
- sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri);
- mUriSliceMap.put(sliceUri, sliceLiveData);
- }
- return sliceLiveData;
- }
-
- /**
- * Start search session
- */
- public void start() {
- stop();
- mLogInstanceId = new InstanceIdSequence().newInstanceId();
- mSearchWidgetHost = new SearchWidgetHost(mLauncher);
- mSearchWidgetHost.startListening();
- }
-
- /**
* Stop search session
*/
public void stop() {
+ clearWidgetHost();
+ }
+
+ private void clearWidgetHost() {
if (mSearchWidgetHost != null) {
mSearchWidgetHost.stopListening();
+ mSearchWidgetHost.clearViews();
mSearchWidgetHost.deleteHost();
- for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) {
- placeholder.clearListeners();
- }
mWidgetPlaceholders.clear();
mSearchWidgetHost = null;
}
- for (LiveData<Slice> liveData : mUriSliceMap.values()) {
- liveData.removeObservers(mLauncher);
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ mPrevLauncherState = mLauncher.getStateManager().getCurrentStableState();
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState != ALL_APPS) {
+ // Clear all search session related objects
+ mUriSliceMap.values().forEach(SliceLifeCycle::destroy);
+ mUriSliceMap.clear();
+
+ clearWidgetHost();
}
- mUriSliceMap.clear();
+
+ StatsLogger logger = mLauncher.getStatsLogManager().logger();
+ if (finalState.equals(ALL_APPS)) {
+ mLogInstanceId = new InstanceIdSequence().newInstanceId();
+ logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_ENTRY);
+ } else if (mPrevLauncherState.equals(ALL_APPS)) {
+ logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_EXIT);
+ mLogInstanceId = null;
+ }
+ }
+
+ /**
+ * Adds a new observer for the provided uri and returns a callback to cancel this observer
+ */
+ public SafeCloseable addObserver(Uri uri, Observer<Slice> listener) {
+ SliceLifeCycle slc = mUriSliceMap.get(uri);
+ if (slc == null) {
+ slc = new SliceLifeCycle(uri, mLauncher);
+ mUriSliceMap.put(uri, slc);
+ }
+ slc.addListener(listener);
+
+ final SliceLifeCycle sliceLifeCycle = slc;
+ return () -> sliceLifeCycle.removeListener(listener);
}
/**
@@ -159,5 +196,121 @@
AppWidgetProviderInfo appWidget) {
return new SearchWidgetInfoContainer(context);
}
+
+ @Override
+ public void clearViews() {
+ super.clearViews();
+ }
+ }
+
+ private static class SliceLifeCycle
+ implements ActivityLifecycleCallbacks, SliceCallback {
+
+ private final Uri mUri;
+ private final Launcher mLauncher;
+ private final SliceViewManager mSliceViewManager;
+ private final ArrayList<Observer<Slice>> mListeners = new ArrayList<>();
+
+ private boolean mDestroyed = false;
+ private boolean mWasListening = false;
+
+ SliceLifeCycle(Uri uri, Launcher launcher) {
+ mUri = uri;
+ mLauncher = launcher;
+ mSliceViewManager = SliceViewManager.getInstance(launcher);
+ launcher.registerActivityLifecycleCallbacks(this);
+
+ if (launcher.isDestroyed()) {
+ onActivityDestroyed(launcher);
+ } else if (launcher.isStarted()) {
+ onActivityStarted(launcher);
+ }
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ destroy();
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ updateListening();
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ updateListening();
+ }
+
+ private void updateListening() {
+ boolean isListening = mDestroyed
+ ? false
+ : (mLauncher.isStarted() && !mListeners.isEmpty());
+ UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening));
+ }
+
+ @WorkerThread
+ private void uploadListeningBg(boolean isListening) {
+ if (mWasListening != isListening) {
+ mWasListening = isListening;
+ if (isListening) {
+ mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this);
+ // Update slice one-time on the different thread so that we can display
+ // multiple slices in parallel
+ THREAD_POOL_EXECUTOR.execute(this::updateSlice);
+ } else {
+ mSliceViewManager.unregisterSliceCallback(mUri, this);
+ }
+ }
+ }
+
+ @UiThread
+ private void addListener(Observer<Slice> listener) {
+ mListeners.add(listener);
+ updateListening();
+ }
+
+ @UiThread
+ private void removeListener(Observer<Slice> listener) {
+ mListeners.remove(listener);
+ updateListening();
+ }
+
+ @WorkerThread
+ private void updateSlice() {
+ try {
+ Slice s = mSliceViewManager.bindSlice(mUri);
+ MAIN_EXECUTOR.execute(() -> onSliceUpdated(s));
+ } catch (Exception e) {
+ Log.d(TAG, "Error fetching slice", e);
+ }
+ }
+
+ @UiThread
+ @Override
+ public void onSliceUpdated(@Nullable Slice s) {
+ mListeners.forEach(l -> l.onChanged(s));
+ }
+
+ private void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ mLauncher.unregisterActivityLifecycleCallbacks(this);
+ mListeners.clear();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+ @Override
+ public void onActivityPaused(Activity activity) { }
+
+ @Override
+ public void onActivityResumed(Activity activity) { }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
}
}
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
index b5c2268..8e5f8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
@@ -70,10 +70,4 @@
mListeners.remove(hostView);
}
- /**
- * Removes all AppWidgetHost update listeners
- */
- public void clearListeners() {
- mListeners.clear();
- }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index b61799f..556aff6 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,9 +72,6 @@
"PROMISE_APPS_NEW_INSTALLS", true,
"Adds a promise icon to the home screen for new install sessions.");
- public static final BooleanFlag APPLY_CONFIG_AT_RUNTIME = getDebugFlag(
- "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
-
public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
"QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
@@ -101,9 +98,6 @@
public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag(
"DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps");
- public static final BooleanFlag DISABLE_SLICE_IN_ALLAPPS = getDebugFlag(
- "DISABLE_SLICE_IN_ALLAPPS", true, "Disable slice in all apps");
-
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
"Suggests folder names instead of blank text.");
@@ -133,9 +127,6 @@
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
- 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 = getDebugFlag(
"HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
@@ -175,10 +166,6 @@
"Replace Smartspace with the enhanced version. "
+ "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
- public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
- "ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
- "Use system VelocityTracker's algorithm for motion pause detection.");
-
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
getDebugFlag(
"ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
@@ -217,6 +204,10 @@
public static final BooleanFlag ENABLE_TASKBAR = new DeviceFlag(
"ENABLE_TASKBAR", false, "Allows a system Taskbar to be shown on larger devices.");
+ public static final BooleanFlag ENABLE_OVERVIEW_GRID = new DeviceFlag(
+ "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
+ + "Only applicable on large screen devices.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index ddf44ca..e71c12d 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -85,9 +85,6 @@
private final WorkspaceAndHotseatScrim mWorkspaceScrim;
private final OverviewScrim mOverviewScrim;
- // View that should handle move events
- private View mMoveTarget;
-
/**
* Used to create a new DragLayer from XML.
*
@@ -109,7 +106,6 @@
public void setup(DragController dragController, Workspace workspace) {
mDragController = dragController;
mWorkspaceScrim.setWorkspace(workspace);
- mMoveTarget = workspace;
recreateControllers();
}
@@ -215,12 +211,6 @@
}
@Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- return super.dispatchUnhandledMove(focused, direction)
- || mMoveTarget.dispatchUnhandledMove(focused, direction);
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
ev.offsetLocation(getTranslationX(), 0);
try {
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 959602b..e8ff8da 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -28,6 +28,9 @@
/** Whether or not an accessible drag operation is in progress. */
public boolean isAccessibleDrag = false;
+ /** Whether or not the drag operation is controlled by keyboard. */
+ public boolean isKeyboardDrag = false;
+
/**
* Specifies the start location for a simulated DnD (like system drag or accessibility drag),
* null when using internal DnD
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 8013557..61f2c2a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -134,7 +133,7 @@
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
* @return a request ID that can be used to cancel the request.
*/
- public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+ public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
final ItemInfoWithIcon info) {
Preconditions.assertUIThread();
if (mPendingIconRequestCount <= 0) {
@@ -142,20 +141,18 @@
}
mPendingIconRequestCount ++;
- IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
- @Override
- public void run() {
- if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
- getTitleAndIcon(info, false);
- } else if (info instanceof PackageItemInfo) {
- getTitleAndIconForApp((PackageItemInfo) info, false);
- }
- MAIN_EXECUTOR.execute(() -> {
- caller.reapplyItemInfo(info);
- onEnd();
- });
- }
- };
+ HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+ () -> {
+ if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+ getTitleAndIcon(info, false);
+ } else if (info instanceof PackageItemInfo) {
+ getTitleAndIconForApp((PackageItemInfo) info, false);
+ }
+ return info;
+ },
+ MAIN_EXECUTOR,
+ caller::reapplyItemInfo,
+ this::onIconRequestEnd);
Utilities.postAsyncCallback(mWorkerHandler, request);
return request;
}
@@ -336,12 +333,6 @@
return super.getEntryFromDB(cacheKey, entry, lowRes);
}
- public static abstract class IconLoadRequest extends HandlerRunnable {
- IconLoadRequest(Handler handler, Runnable endRunnable) {
- super(handler, endRunnable);
- }
- }
-
/**
* Interface for receiving itemInfo with high-res icon.
*/
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 800598e..77ce4a8 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -88,6 +88,7 @@
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
- return mDelegate.performAction(mIcon, (ItemInfo) mIcon.getTag(), menuItem.getItemId());
+ return mDelegate.performAction(mIcon, (ItemInfo) mIcon.getTag(), menuItem.getItemId(),
+ true);
}
}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index ae7ad10..83003ff 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -16,233 +16,30 @@
package com.android.launcher3.keyboard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.util.Property;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
/**
* A helper class to draw background of a focused view.
*/
-public abstract class FocusIndicatorHelper implements
- OnFocusChangeListener, AnimatorUpdateListener {
-
- private static final float MIN_VISIBLE_ALPHA = 0.2f;
- private static final long ANIM_DURATION = 150;
-
- public static final Property<FocusIndicatorHelper, Float> ALPHA =
- new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
- @Override
- public void set(FocusIndicatorHelper object, Float value) {
- object.setAlpha(value);
- }
-
- @Override
- public Float get(FocusIndicatorHelper object) {
- return object.mAlpha;
- }
- };
-
- public static final Property<FocusIndicatorHelper, Float> SHIFT =
- new Property<FocusIndicatorHelper, Float>(
- Float.TYPE, "shift") {
-
- @Override
- public void set(FocusIndicatorHelper object, Float value) {
- object.mShift = value;
- }
-
- @Override
- public Float get(FocusIndicatorHelper object) {
- return object.mShift;
- }
- };
-
- private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
- private static final Rect sTempRect1 = new Rect();
- private static final Rect sTempRect2 = new Rect();
-
- private final View mContainer;
- private final Paint mPaint;
- private final int mMaxAlpha;
-
- private final Rect mDirtyRect = new Rect();
- private boolean mIsDirty = false;
-
- private View mLastFocusedView;
-
- private View mCurrentView;
- private View mTargetView;
- /**
- * The fraction indicating the position of the focusRect between {@link #mCurrentView}
- * & {@link #mTargetView}
- */
- private float mShift;
-
- private ObjectAnimator mCurrentAnimation;
- private float mAlpha;
- private float mRadius;
+public abstract class FocusIndicatorHelper extends ItemFocusIndicatorHelper<View>
+ implements OnFocusChangeListener {
public FocusIndicatorHelper(View container) {
- mContainer = container;
-
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- int color = container.getResources().getColor(R.color.focused_background);
- mMaxAlpha = Color.alpha(color);
- mPaint.setColor(0xFF000000 | color);
-
- setAlpha(0);
- mShift = 0;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- mRadius = Themes.getDialogCornerRadius(container.getContext());
- }
- }
-
- protected void setAlpha(float alpha) {
- mAlpha = alpha;
- mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidateDirty();
- }
-
- protected void invalidateDirty() {
- if (mIsDirty) {
- mContainer.invalidate(mDirtyRect);
- mIsDirty = false;
- }
-
- Rect newRect = getDrawRect();
- if (newRect != null) {
- mContainer.invalidate(newRect);
- }
- }
-
- public void draw(Canvas c) {
- if (mAlpha <= 0) return;
-
- Rect newRect = getDrawRect();
- if (newRect != null) {
- mDirtyRect.set(newRect);
- c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
- (float) mDirtyRect.right, (float) mDirtyRect.bottom,
- mRadius, mRadius, mPaint);
- mIsDirty = true;
- }
- }
-
- private Rect getDrawRect() {
- if (mCurrentView != null && mCurrentView.isAttachedToWindow()) {
- viewToRect(mCurrentView, sTempRect1);
-
- if (mShift > 0 && mTargetView != null) {
- viewToRect(mTargetView, sTempRect2);
- return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
- } else {
- return sTempRect1;
- }
- }
- return null;
+ super(container, container.getResources().getColor(R.color.focused_background));
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- endCurrentAnimation();
-
- if (mAlpha > MIN_VISIBLE_ALPHA) {
- mTargetView = v;
-
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 1),
- PropertyValuesHolder.ofFloat(SHIFT, 1));
- mCurrentAnimation.addListener(new ViewSetListener(v, true));
- } else {
- setCurrentView(v);
-
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 1));
- }
-
- mLastFocusedView = v;
- } else {
- if (mLastFocusedView == v) {
- mLastFocusedView = null;
- endCurrentAnimation();
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 0));
- mCurrentAnimation.addListener(new ViewSetListener(null, false));
- }
- }
-
- // invalidate once
- invalidateDirty();
-
- mLastFocusedView = hasFocus ? v : null;
- if (mCurrentAnimation != null) {
- mCurrentAnimation.addUpdateListener(this);
- mCurrentAnimation.setDuration(ANIM_DURATION).start();
- }
+ changeFocus(v, hasFocus);
}
- protected void endCurrentAnimation() {
- if (mCurrentAnimation != null) {
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
- }
-
- protected void setCurrentView(View v) {
- mCurrentView = v;
- mShift = 0;
- mTargetView = null;
- }
-
- /**
- * Gets the position of {@param v} relative to {@link #mContainer}.
- */
- public abstract void viewToRect(View v, Rect outRect);
-
- private class ViewSetListener extends AnimatorListenerAdapter {
- private final View mViewToSet;
- private final boolean mCallOnCancel;
- private boolean mCalled = false;
-
- public ViewSetListener(View v, boolean callOnCancel) {
- mViewToSet = v;
- mCallOnCancel = callOnCancel;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (!mCallOnCancel) {
- mCalled = true;
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCalled) {
- setCurrentView(mViewToSet);
- mCalled = true;
- }
- }
+ @Override
+ protected boolean shouldDraw(View item) {
+ return item.isAttachedToWindow();
}
/**
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
new file mode 100644
index 0000000..57fab2d
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2021 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.keyboard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Themes;
+
+/**
+ * A helper class to draw background of a focused item.
+ * @param <T> Item type
+ */
+public abstract class ItemFocusIndicatorHelper<T> implements AnimatorUpdateListener {
+
+ private static final float MIN_VISIBLE_ALPHA = 0.2f;
+ private static final long ANIM_DURATION = 150;
+
+ public static final FloatProperty<ItemFocusIndicatorHelper> ALPHA =
+ new FloatProperty<ItemFocusIndicatorHelper>("alpha") {
+
+ @Override
+ public void setValue(ItemFocusIndicatorHelper object, float value) {
+ object.setAlpha(value);
+ }
+
+ @Override
+ public Float get(ItemFocusIndicatorHelper object) {
+ return object.mAlpha;
+ }
+ };
+
+ public static final FloatProperty<ItemFocusIndicatorHelper> SHIFT =
+ new FloatProperty<ItemFocusIndicatorHelper>("shift") {
+
+ @Override
+ public void setValue(ItemFocusIndicatorHelper object, float value) {
+ object.mShift = value;
+ }
+
+ @Override
+ public Float get(ItemFocusIndicatorHelper object) {
+ return object.mShift;
+ }
+ };
+
+ private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+ private static final Rect sTempRect1 = new Rect();
+ private static final Rect sTempRect2 = new Rect();
+
+ private final View mContainer;
+ protected final Paint mPaint;
+ private final int mMaxAlpha;
+
+ private final Rect mDirtyRect = new Rect();
+ private boolean mIsDirty = false;
+
+ private T mLastFocusedItem;
+
+ private T mCurrentItem;
+ private T mTargetItem;
+ /**
+ * The fraction indicating the position of the focusRect between {@link #mCurrentItem}
+ * & {@link #mTargetItem}
+ */
+ private float mShift;
+
+ private ObjectAnimator mCurrentAnimation;
+ private float mAlpha;
+ private float mRadius;
+
+ public ItemFocusIndicatorHelper(View container, int color) {
+ mContainer = container;
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mMaxAlpha = Color.alpha(color);
+ mPaint.setColor(0xFF000000 | color);
+
+ setAlpha(0);
+ mShift = 0;
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ mRadius = Themes.getDialogCornerRadius(container.getContext());
+ }
+ }
+
+ protected void setAlpha(float alpha) {
+ mAlpha = alpha;
+ mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidateDirty();
+ }
+
+ protected void invalidateDirty() {
+ if (mIsDirty) {
+ mContainer.invalidate(mDirtyRect);
+ mIsDirty = false;
+ }
+
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mContainer.invalidate(newRect);
+ }
+ }
+
+ /**
+ * Draws the indicator on the canvas
+ */
+ public void draw(Canvas c) {
+ if (mAlpha <= 0) return;
+
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mDirtyRect.set(newRect);
+ c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
+ (float) mDirtyRect.right, (float) mDirtyRect.bottom,
+ mRadius, mRadius, mPaint);
+ mIsDirty = true;
+ }
+ }
+
+ private Rect getDrawRect() {
+ if (mCurrentItem != null && shouldDraw(mCurrentItem)) {
+ viewToRect(mCurrentItem, sTempRect1);
+
+ if (mShift > 0 && mTargetItem != null) {
+ viewToRect(mTargetItem, sTempRect2);
+ return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
+ } else {
+ return sTempRect1;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the provided item is valid
+ */
+ protected boolean shouldDraw(T item) {
+ return true;
+ }
+
+ protected void changeFocus(T item, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+
+ if (mAlpha > MIN_VISIBLE_ALPHA) {
+ mTargetItem = item;
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1),
+ PropertyValuesHolder.ofFloat(SHIFT, 1));
+ mCurrentAnimation.addListener(new ViewSetListener(item, true));
+ } else {
+ setCurrentItem(item);
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1));
+ }
+
+ mLastFocusedItem = item;
+ } else {
+ if (mLastFocusedItem == item) {
+ mLastFocusedItem = null;
+ endCurrentAnimation();
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 0));
+ mCurrentAnimation.addListener(new ViewSetListener(null, false));
+ }
+ }
+
+ // invalidate once
+ invalidateDirty();
+
+ mLastFocusedItem = hasFocus ? item : null;
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.addUpdateListener(this);
+ mCurrentAnimation.setDuration(ANIM_DURATION).start();
+ }
+ }
+
+ protected void endCurrentAnimation() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ }
+
+ protected void setCurrentItem(T item) {
+ mCurrentItem = item;
+ mShift = 0;
+ mTargetItem = null;
+ }
+
+ /**
+ * Gets the position of the item relative to {@link #mContainer}.
+ */
+ public abstract void viewToRect(T item, Rect outRect);
+
+ private class ViewSetListener extends AnimatorListenerAdapter {
+ private final T mItemToSet;
+ private final boolean mCallOnCancel;
+ private boolean mCalled = false;
+
+ ViewSetListener(T item, boolean callOnCancel) {
+ mItemToSet = item;
+ mCallOnCancel = callOnCancel;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!mCallOnCancel) {
+ mCalled = true;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCalled) {
+ setCurrentItem(mItemToSet);
+ mCalled = true;
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
new file mode 100644
index 0000000..a6c897f
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2021 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.keyboard;
+
+import static android.app.Activity.DEFAULT_KEYS_SEARCH_LOCAL;
+
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.Themes;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.function.ToIntBiFunction;
+import java.util.function.ToIntFunction;
+
+/**
+ * A floating view to allow keyboard navigation across virtual nodes
+ */
+public class KeyboardDragAndDropView extends AbstractFloatingView
+ implements Insettable, StateListener<LauncherState> {
+
+ private static final long MINOR_AXIS_WEIGHT = 13;
+
+ private final ArrayList<Integer> mIntList = new ArrayList<>();
+ private final ArrayList<DragAndDropAccessibilityDelegate> mDelegates = new ArrayList<>();
+ private final ArrayList<VirtualNodeInfo> mNodes = new ArrayList<>();
+
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect2 = new Rect();
+ private final AccessibilityNodeInfoCompat mTempNodeInfo = AccessibilityNodeInfoCompat.obtain();
+
+ private final RectFocusIndicator mFocusIndicator;
+
+ private final Launcher mLauncher;
+ private VirtualNodeInfo mCurrentSelection;
+
+
+ public KeyboardDragAndDropView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyboardDragAndDropView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+ mFocusIndicator = new RectFocusIndicator(this);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mLauncher.getDragLayer().removeView(this);
+ mLauncher.getStateManager().removeStateListener(this);
+ mLauncher.setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+ mIsOpen = false;
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_DRAG_DROP_POPUP) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ // Consume all touch
+ return true;
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ if (toState != SPRING_LOADED) {
+ close(false);
+ }
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (mCurrentSelection != null) {
+ setCurrentSelection(mCurrentSelection);
+ }
+ }
+
+ private void setCurrentSelection(VirtualNodeInfo nodeInfo) {
+ mCurrentSelection = nodeInfo;
+ ((TextView) findViewById(R.id.label))
+ .setText(nodeInfo.populate(mTempNodeInfo).getContentDescription());
+
+ Rect bounds = new Rect();
+ mTempNodeInfo.getBoundsInParent(bounds);
+ View host = nodeInfo.delegate.getHost();
+ ViewParent parent = host.getParent();
+ if (parent instanceof PagedView) {
+ PagedView pv = (PagedView) parent;
+ int pageIndex = pv.indexOfChild(host);
+
+ pv.setCurrentPage(pageIndex);
+ bounds.offset(pv.getScrollX() - pv.getScrollForPage(pageIndex), 0);
+ }
+ float[] pos = new float[] {bounds.left, bounds.top, bounds.right, bounds.bottom};
+ Utilities.getDescendantCoordRelativeToAncestor(host, mLauncher.getDragLayer(), pos, true);
+
+ new RectF(pos[0], pos[1], pos[2], pos[3]).roundOut(bounds);
+ mFocusIndicator.changeFocus(bounds, true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mFocusIndicator.draw(canvas);
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ VirtualNodeInfo nodeInfo = getNextSelection(direction);
+ if (nodeInfo == null) {
+ return false;
+ }
+ setCurrentSelection(nodeInfo);
+ return true;
+ }
+
+ /**
+ * Focus finding logic:
+ * Collect all virtual nodes in reading order (used for forward and backwards).
+ * Then find the closest view by comparing the distances spatially. Since it is a move
+ * operation. consider all cell sizes to be approximately of the same size.
+ */
+ private VirtualNodeInfo getNextSelection(int direction) {
+ // Collect all virtual nodes
+ mDelegates.clear();
+ mNodes.clear();
+
+ Folder openFolder = Folder.getOpen(mLauncher);
+ PagedView pv = openFolder == null ? mLauncher.getWorkspace() : openFolder.getContent();
+ int count = pv.getPageCount();
+ for (int i = 0; i < count; i++) {
+ mDelegates.add(((CellLayout) pv.getChildAt(i)).getDragAndDropAccessibilityDelegate());
+ }
+ if (openFolder == null) {
+ mDelegates.add(pv.getNextPage() + 1,
+ mLauncher.getHotseat().getDragAndDropAccessibilityDelegate());
+ }
+ mDelegates.forEach(delegate -> {
+ mIntList.clear();
+ delegate.getVisibleVirtualViews(mIntList);
+ mIntList.forEach(id -> mNodes.add(new VirtualNodeInfo(delegate, id)));
+ });
+
+ if (mNodes.isEmpty()) {
+ return null;
+ }
+ int index = mNodes.indexOf(mCurrentSelection);
+ if (mCurrentSelection == null || index < 0) {
+ return null;
+ }
+ int totalNodes = mNodes.size();
+
+ final ToIntBiFunction<Rect, Rect> majorAxis;
+ final ToIntFunction<Rect> minorAxis;
+
+ switch (direction) {
+ case View.FOCUS_RIGHT:
+ majorAxis = (source, dest) -> dest.left - source.left;
+ minorAxis = Rect::centerY;
+ break;
+ case View.FOCUS_LEFT:
+ majorAxis = (source, dest) -> source.left - dest.left;
+ minorAxis = Rect::centerY;
+ break;
+ case View.FOCUS_UP:
+ majorAxis = (source, dest) -> source.top - dest.top;
+ minorAxis = Rect::centerX;
+ break;
+ case View.FOCUS_DOWN:
+ majorAxis = (source, dest) -> dest.top - source.top;
+ minorAxis = Rect::centerX;
+ break;
+ case View.FOCUS_FORWARD:
+ return mNodes.get((index + 1) % totalNodes);
+ case View.FOCUS_BACKWARD:
+ return mNodes.get((index + totalNodes - 1) % totalNodes);
+ default:
+ // Unknown direction
+ return null;
+ }
+ mCurrentSelection.populate(mTempNodeInfo).getBoundsInScreen(mTempRect);
+
+ float minWeight = Float.MAX_VALUE;
+ VirtualNodeInfo match = null;
+ for (int i = 0; i < totalNodes; i++) {
+ VirtualNodeInfo node = mNodes.get(i);
+ node.populate(mTempNodeInfo).getBoundsInScreen(mTempRect2);
+
+ int majorAxisWeight = majorAxis.applyAsInt(mTempRect, mTempRect2);
+ if (majorAxisWeight <= 0) {
+ continue;
+ }
+ int minorAxisWeight = minorAxis.applyAsInt(mTempRect2)
+ - minorAxis.applyAsInt(mTempRect);
+
+ float weight = majorAxisWeight * majorAxisWeight
+ + minorAxisWeight * minorAxisWeight * MINOR_AXIS_WEIGHT;
+ if (weight < minWeight) {
+ minWeight = weight;
+ match = node;
+ }
+ }
+ return match;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && mCurrentSelection != null) {
+ mCurrentSelection.delegate.onPerformActionForVirtualView(
+ mCurrentSelection.id, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ /**
+ * Shows the keyboard drag popup for the provided view
+ */
+ public void showForIcon(View icon, ItemInfo item, DragOptions dragOptions) {
+ mIsOpen = true;
+ mLauncher.getDragLayer().addView(this);
+ mLauncher.getStateManager().addStateListener(this);
+
+ // Find current selection
+ CellLayout currentParent = (CellLayout) icon.getParent().getParent();
+ float[] iconPos = new float[] {currentParent.getCellWidth() / 2,
+ currentParent.getCellHeight() / 2};
+ Utilities.getDescendantCoordRelativeToAncestor(icon, currentParent, iconPos, false);
+
+ ItemLongClickListener.beginDrag(icon, mLauncher, item, dragOptions);
+
+ DragAndDropAccessibilityDelegate dndDelegate =
+ currentParent.getDragAndDropAccessibilityDelegate();
+ setCurrentSelection(new VirtualNodeInfo(
+ dndDelegate, dndDelegate.getVirtualViewAt(iconPos[0], iconPos[1])));
+
+ mLauncher.setDefaultKeyMode(Activity.DEFAULT_KEYS_DISABLE);
+ requestFocus();
+ }
+
+ private static class VirtualNodeInfo {
+ public final DragAndDropAccessibilityDelegate delegate;
+ public final int id;
+
+ VirtualNodeInfo(DragAndDropAccessibilityDelegate delegate, int id) {
+ this.id = id;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof VirtualNodeInfo)) {
+ return false;
+ }
+ VirtualNodeInfo that = (VirtualNodeInfo) o;
+ return id == that.id && delegate.equals(that.delegate);
+ }
+
+ public AccessibilityNodeInfoCompat populate(AccessibilityNodeInfoCompat nodeInfo) {
+ delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+ return nodeInfo;
+ }
+
+ public void getBounds(AccessibilityNodeInfoCompat nodeInfo, Rect out) {
+ delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+ nodeInfo.getBoundsInScreen(out);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, delegate);
+ }
+ }
+
+ private static class RectFocusIndicator extends ItemFocusIndicatorHelper<Rect> {
+
+ RectFocusIndicator(View container) {
+ super(container, Themes.getColorAccent(container.getContext()));
+ mPaint.setStrokeWidth(container.getResources()
+ .getDimension(R.dimen.keyboard_drag_stroke_width));
+ mPaint.setStyle(Style.STROKE);
+ }
+
+ @Override
+ public void viewToRect(Rect item, Rect outRect) {
+ outRect.set(item);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 6bc1ecb..cdd0bda 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -10,7 +10,6 @@
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -43,7 +42,7 @@
private static Handler sHandler = null;
private static File sLogsDirectory = null;
- public static final int LOG_DAYS = FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() ? 10 : 4;
+ public static final int LOG_DAYS = 4;
public static void setDir(File logsDir) {
if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2066cd3..0292d20 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -344,6 +344,12 @@
@UiEvent(doc = "Current grid size is changed to 2.")
LAUNCHER_GRID_SIZE_2(665),
+
+ @UiEvent(doc = "Launcher entered into AllApps state.")
+ LAUNCHER_ALLAPPS_ENTRY(692),
+
+ @UiEvent(doc = "Launcher exited from AllApps state.")
+ LAUNCHER_ALLAPPS_EXIT(693),
;
// ADD MORE
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 3851ab0..00ac12f 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -26,6 +26,7 @@
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.EXTENDED_CONTAINERS;
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;
@@ -54,6 +55,7 @@
import com.android.launcher3.logger.LauncherAtom.Shortcut;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
@@ -403,12 +405,23 @@
return ContainerInfo.newBuilder()
.setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
.build();
-
+ case EXTENDED_CONTAINERS:
+ return ContainerInfo.newBuilder()
+ .setExtendedContainers(getExtendedContainer())
+ .build();
}
return ContainerInfo.getDefaultInstance();
}
/**
+ * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden
+ * by build variants.
+ */
+ protected ExtendedContainers getExtendedContainer() {
+ return ExtendedContainers.getDefaultInstance();
+ }
+
+ /**
* Returns shallow copy of the object.
*/
public ItemInfo makeShallowCopy() {
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index 8469569..25355c9 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model.data;
+import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
+
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.Icon;
@@ -45,6 +47,7 @@
CharSequence title) {
this.user = user == null ? Process.myUserHandle() : user;
this.title = title;
+ this.container = EXTENDED_CONTAINERS;
mFallbackPackageName = packageName;
mIcon = icon;
}
@@ -55,6 +58,7 @@
mFallbackPackageName = info.mFallbackPackageName;
mFlags = info.mFlags;
title = info.title;
+ this.container = EXTENDED_CONTAINERS;
}
/**
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 5464dd8..15f7730 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -455,12 +455,6 @@
}
@Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- // Consume the unhandled move if a container is open, to avoid switching pages underneath.
- return AbstractFloatingView.getTopOpenView(mActivity) != null;
- }
-
- @Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
diff --git a/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..b90e43b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+ int VERSION = 1;
+
+ /** Register a listener to get Smartspace data. */
+ void registerListener(SmartspaceTargetListener listener);
+
+ /** Unregister a listener. */
+ void unregisterListener(SmartspaceTargetListener listener);
+
+ /** Provides Smartspace data to registered listeners. */
+ interface SmartspaceTargetListener {
+ /** Each Parcelable is a SmartspaceTarget that represents a card. */
+ void onSmartspaceTargetsUpdated(List<Parcelable> targets);
+ }
+}