Merge "Add Logs to track app uninstall and icon removal" into ub-launcher3-qt-future-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 371161e..7115943 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -18,16 +18,11 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
-import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -35,18 +30,17 @@
import android.content.Context;
import android.view.View;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
* {@link RecentsView}.
@@ -156,8 +150,11 @@
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
case INDEX_RECENTS_TRANSLATE_X_ANIM:
- return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
- VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
+ return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
+ .setDampingRatio(0.8f)
+ .setStiffness(250)
+ .setValues(values)
+ .build(mLauncher);
default:
return super.createStateElementAnimation(index, values);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 085bbc4..1a59770 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -24,7 +24,6 @@
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
@@ -32,6 +31,8 @@
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
@@ -58,7 +59,7 @@
* 4) Maintains the current active client id (for the predictions) and all updates are performed on
* that client id.
*/
-public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
+public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
OnIDPChangeListener, OnUpdateListener {
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
@@ -153,7 +154,10 @@
public void reapplyItemInfo(ItemInfoWithIcon info) { }
@Override
- public void onGlobalLayout() {
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState state) {
if (mAppsView == null) {
return;
}
@@ -162,7 +166,8 @@
mPendingState = null;
}
if (mPendingState == null) {
- mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ Launcher.getLauncher(mAppsView.getContext()).getStateManager()
+ .removeStateListener(this);
}
}
@@ -170,9 +175,8 @@
boolean registerListener = mPendingState == null;
mPendingState = state;
if (registerListener) {
- // OnGlobalLayoutListener is called whenever a view in the view tree changes
- // visibility. Add a listener and wait until appsView is invisible again.
- mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+ // Add a listener and wait until appsView is invisible again.
+ Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 50cfac8..468b8af 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -19,7 +19,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.LayoutUtils;
@@ -69,8 +68,16 @@
if (taskCount == 0) {
return super.getOverviewScaleAndTranslation(launcher);
}
- TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange(
- recentsView.getCurrentPage(), 0, taskCount - 1));
+ TaskView dummyTask;
+ if (recentsView.getCurrentPage() >= 0) {
+ if (recentsView.getCurrentPage() <= taskCount - 1) {
+ dummyTask = recentsView.getCurrentPageTaskView();
+ } else {
+ dummyTask = recentsView.getTaskViewAt(taskCount - 1);
+ }
+ } else {
+ dummyTask = recentsView.getTaskViewAt(0);
+ }
return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
.getScaleAndTranslation();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 20a2487..03862db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -47,9 +47,9 @@
* @return true if we should intercept the motion event
*/
boolean canInterceptTouch(MotionEvent ev) {
- if (mRecentsView.getChildCount() > 0) {
+ if (mRecentsView.getTaskViewCount() > 0) {
// Allow swiping up in the gap between the hotseat and overview.
- return ev.getY() >= mRecentsView.getChildAt(0).getBottom();
+ return ev.getY() >= mRecentsView.getTaskViewAt(0).getBottom();
} else {
// If there are no tasks, we only intercept if we're below the hotseat height.
return isTouchOverHotseat(mLauncher, ev);
@@ -63,7 +63,7 @@
* @return true if going back should take the user to the currently running task
*/
boolean shouldSwipeDownReturnToApp() {
- TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+ TaskView taskView = mRecentsView.getNextPageTaskView();
return taskView != null && mRecentsView.shouldSwipeDownLaunchApp();
}
@@ -76,7 +76,7 @@
*/
PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
- TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage());
+ TaskView taskView = mRecentsView.getCurrentPageTaskView();
if (taskView == null) {
throw new IllegalStateException("There is no task view to animate to.");
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index eb571f6..6576ec5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -50,7 +50,6 @@
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.QuickStepContract;
/**
* Handles quick switching to a recent task from the home screen.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 676ea69..7196f7c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -227,10 +227,10 @@
// Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
- mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
+ mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
- int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
+ int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
mFinishingRecentsAnimationForNewTaskId = taskId;
mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
if (!mCanceled) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 25cc33d..c17b2fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -268,7 +268,7 @@
INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
int runningTaskIndex = recentsView.getRunningTaskIndex();
- if (runningTaskIndex == 0) {
+ if (runningTaskIndex == recentsView.getTaskViewStartIndex()) {
// If we are on the first task (we haven't quick switched), translate recents in
// from the side. Calculate the start translation based on current scale/scroll.
float currScale = recentsView.getScaleX();
@@ -351,8 +351,7 @@
private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState,
LauncherState endState) {
RecentsView recentsView = launcher.getOverviewPanel();
- TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage());
- if (v == null) {
+ if (recentsView.getCurrentPageTaskView() == null) {
return;
}
@@ -380,7 +379,7 @@
// recents as a whole needs to translate further to keep up with the app window.
TaskView runningTaskView = recentsView.getRunningTaskView();
if (runningTaskView == null) {
- runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage());
+ runningTaskView = recentsView.getCurrentPageTaskView();
}
TimeInterpolator oldInterpolator = translateY.getInterpolator();
Rect fallbackInsets = launcher.getDeviceProfile().getInsets();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c18b604..79273ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -109,7 +109,7 @@
TaskView taskView = rv.getNextTaskView();
if (taskView == null) {
if (rv.getTaskViewCount() > 0) {
- taskView = (TaskView) rv.getPageAt(0);
+ taskView = rv.getTaskViewAt(0);
taskView.requestFocus();
} else {
rv.requestFocus();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index a3bd348..411a808 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -479,8 +479,8 @@
recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
if (animate) {
// Only animate if an adjacent task view is visible on screen.
- TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
- TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
+ TaskView adjacentTask1 = mRecentsView.getNextTaskView();
+ TaskView adjacentTask2 = mRecentsView.getPreviousTaskView();
float prevTranslationX = mRecentsView.getTranslationX();
mRecentsView.setTranslationX(0);
animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT))
@@ -590,8 +590,7 @@
*/
private void updateSysUiFlags(float windowProgress) {
if (mRecentsView != null) {
- TaskView centermostTask = mRecentsView.getTaskViewAt(mRecentsView
- .getPageNearestToCenterOfScreen());
+ TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
int centermostTaskFlags = centermostTask == null ? 0
: centermostTask.getThumbnail().getSysUiStatusNavFlags();
boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 2e9c0a3..7f1aae5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -176,7 +176,7 @@
protected void applyLoadPlan(ArrayList<Task> tasks) {
// When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
- // track the index of the next task appropriately, as it we are switching on any other app.
+ // track the index of the next task appropriately, as if we are switching on any other app.
if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) {
// Check if the task list has running task
boolean found = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 9464bd1..804d1e0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -306,6 +306,9 @@
private Layout mEmptyTextLayout;
private LiveTileOverlay mLiveTileOverlay;
+ // Keeps track of the index where the first TaskView should be
+ private int mTaskViewStartIndex = 0;
+
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
if (!inMultiWindowMode && mOverviewStateEnabled) {
@@ -329,7 +332,6 @@
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
-
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
@@ -430,7 +432,7 @@
super.onViewRemoved(child);
// Clear the task data for the removed child if it was visible
- if (child != mClearAllButton) {
+ if (child instanceof TaskView) {
TaskView taskView = (TaskView) child;
mHasVisibleTaskData.delete(taskView.getTask().key.id);
mTaskViewPool.recycle(taskView);
@@ -444,7 +446,7 @@
public TaskView getTaskView(int taskId) {
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView tv = (TaskView) getChildAt(i);
+ TaskView tv = getTaskViewAt(i);
if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) {
return tv;
}
@@ -536,28 +538,26 @@
}
if (tasks == null || tasks.isEmpty()) {
- removeAllViews();
+ removeTasksViewsAndClearAllButton();
onTaskStackUpdated();
return;
}
- int oldChildCount = getChildCount();
-
// Unload existing visible task data
unloadVisibleTaskData();
- TaskView ignoreRestTaskView =
+ TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
final int requiredTaskCount = tasks.size();
if (getTaskViewCount() != requiredTaskCount) {
- if (oldChildCount > 0) {
+ if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
}
- for (int i = getChildCount(); i < requiredTaskCount; i++) {
+ for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
addView(mTaskViewPool.getView());
}
- while (getChildCount() > requiredTaskCount) {
+ while (getTaskViewCount() > requiredTaskCount) {
removeView(getChildAt(getChildCount() - 1));
}
if (requiredTaskCount > 0) {
@@ -567,17 +567,20 @@
// Rebind and reset all task views
for (int i = requiredTaskCount - 1; i >= 0; i--) {
- final int pageIndex = requiredTaskCount - i - 1;
+ final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
final Task task = tasks.get(i);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task);
}
+
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
setCurrentPage(indexOfChild(runningTaskView));
+ } else if (getTaskViewCount() > 0) {
+ setCurrentPage(indexOfChild(getTaskViewAt(0)));
}
- if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) {
+ if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) {
// If the taskView mapping is changing, do not preserve the visuals. Since we are
// mostly preserving the first task, and new taskViews are added to the end, it should
// generally map to the same task.
@@ -588,17 +591,28 @@
updateEnabledOverlays();
}
+ private void removeTasksViewsAndClearAllButton() {
+ for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+ removeView(getTaskViewAt(i));
+ }
+ if (indexOfChild(mClearAllButton) != -1) {
+ removeView(mClearAllButton);
+ }
+ }
+
public int getTaskViewCount() {
- // Account for the clear all button.
- int childCount = getChildCount();
- return childCount == 0 ? 0 : childCount - 1;
+ int taskViewCount = getChildCount() - mTaskViewStartIndex;
+ if (indexOfChild(mClearAllButton) != -1) {
+ taskViewCount--;
+ }
+ return taskViewCount;
}
protected void onTaskStackUpdated() { }
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
- TaskView taskView = (TaskView) getChildAt(i);
+ TaskView taskView = getTaskViewAt(i);
if (mIgnoreResetTaskId != taskView.getTask().key.id) {
taskView.resetVisualProperties();
taskView.setStableAlpha(mContentAlpha);
@@ -704,7 +718,7 @@
}
/**
- * Iterates through all thet asks, and loads the associated task data for newly visible tasks,
+ * Iterates through all the tasks, and loads the associated task data for newly visible tasks,
* and unloads the associated task data for tasks that are no longer visible.
*/
public void loadVisibleTaskData() {
@@ -715,15 +729,16 @@
}
int centerPageIndex = getPageNearestToCenterOfScreen();
- int numChildren = getTaskViewCount();
+ int numChildren = getChildCount();
int lower = Math.max(0, centerPageIndex - 2);
int upper = Math.min(centerPageIndex + 2, numChildren - 1);
// Update the task data for the in/visible children
- for (int i = 0; i < numChildren; i++) {
- TaskView taskView = (TaskView) getChildAt(i);
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView taskView = getTaskViewAt(i);
Task task = taskView.getTask();
- boolean visible = lower <= i && i <= upper;
+ int index = indexOfChild(taskView);
+ boolean visible = lower <= index && index <= upper;
if (visible) {
if (task == mTmpRunningTask) {
// Skip loading if this is the task that we are animating into
@@ -798,6 +813,10 @@
return tv == null ? -1 : indexOfChild(tv);
}
+ public int getTaskViewStartIndex() {
+ return mTaskViewStartIndex;
+ }
+
/**
* Reloads the view if anything in recents changed.
*/
@@ -853,10 +872,10 @@
*/
public void showCurrentTask(int runningTaskId) {
if (getTaskView(runningTaskId) == null) {
- boolean wasEmpty = getChildCount() == 0;
+ boolean wasEmpty = getTaskViewCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
- addView(taskView, 0);
+ addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
}
@@ -922,14 +941,13 @@
if (runningTaskView == null) {
// Launch the first task
if (getTaskViewCount() > 0) {
- getTaskViewAt(0).launchTask(true /* animate */);
+ getTaskViewAt(0).launchTask(true);
}
} else {
- TaskView nextTaskView = getNextTaskView();
- if (nextTaskView != null) {
- nextTaskView.launchTask(true /* animate */);
+ if (getNextTaskView() != null) {
+ getNextTaskView().launchTask(true);
} else {
- runningTaskView.launchTask(true /* animate */);
+ runningTaskView.launchTask(true);
}
}
}
@@ -1189,7 +1207,7 @@
int count = getTaskViewCount();
for (int i = 0; i < count; i++) {
- addDismissedTaskAnimations(getChildAt(i), anim, duration);
+ addDismissedTaskAnimations(getTaskViewAt(i), anim, duration);
}
mPendingAnimation = pendingAnimation;
@@ -1197,7 +1215,7 @@
if (onEndListener.isSuccess) {
// Remove all the task views now
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
- removeAllViews();
+ removeTasksViewsAndClearAllButton();
startHome();
}
mPendingAnimation = null;
@@ -1344,26 +1362,50 @@
child.setAlpha(mContentAlpha);
}
- /**
- * @return The most recent task that is older than the currently running task. If there is
- * currently no running task or there is no task older than it, then return null.
- */
@Nullable
public TaskView getNextTaskView() {
- TaskView runningTaskView = getRunningTaskView();
- if (runningTaskView == null) {
- return null;
- }
- return getTaskViewAt(indexOfChild(runningTaskView) + 1);
+ return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1);
}
+ @Nullable
+ public TaskView getPreviousTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() - 1);
+ }
+
+ @Nullable
+ public TaskView getCurrentPageTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getCurrentPage());
+ }
+
+ @Nullable
+ public TaskView getNextPageTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getNextPage());
+ }
+
+ @Nullable
+ public TaskView getTaskViewNearestToCenterOfScreen() {
+ return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen());
+ }
+
+ /**
+ * Returns null instead of indexOutOfBoundsError when index is not in range
+ */
+ @Nullable
public TaskView getTaskViewAt(int index) {
- View child = getChildAt(index);
- return child == mClearAllButton ? null : (TaskView) child;
+ return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
+ }
+
+ @Nullable
+ private TaskView getTaskViewAtByAbsoluteIndex(int index) {
+ if (index < getChildCount() && index >= 0) {
+ View child = getChildAt(index);
+ return child instanceof TaskView ? (TaskView) child : null;
+ }
+ return null;
}
public void updateEmptyMessage() {
- boolean isEmpty = getChildCount() == 0;
+ boolean isEmpty = getTaskViewCount() == 0;
boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
|| mLastMeasureSize.y != getHeight();
if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
@@ -1497,7 +1539,7 @@
throw new IllegalStateException("Another pending animation is still running");
}
- int count = getChildCount();
+ int count = getTaskViewCount();
if (count == 0) {
return new PendingAnimation(new AnimatorSet());
}
@@ -1670,18 +1712,38 @@
@Override
protected int computeMinScrollX() {
- if (mIsRtl && mDisallowScrollToClearAll) {
- // We aren't showing the clear all button, so use the leftmost task as the min scroll.
- return getScrollForPage(getTaskViewCount() - 1);
+ if (getTaskViewCount() > 0) {
+ if (mDisallowScrollToClearAll) {
+ // We aren't showing the clear all button,
+ // so use the leftmost task as the min scroll.
+ if (mIsRtl) {
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
+ }
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ if (mIsRtl) {
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
+ }
+ return getScrollForPage(mTaskViewStartIndex);
}
return super.computeMinScrollX();
}
@Override
protected int computeMaxScrollX() {
- if (!mIsRtl && mDisallowScrollToClearAll) {
- // We aren't showing the clear all button, so use the rightmost task as the max scroll.
- return getScrollForPage(getTaskViewCount() - 1);
+ if (getTaskViewCount() > 0) {
+ if (mDisallowScrollToClearAll) {
+ // We aren't showing the clear all button,
+ // so use the rightmost task as the min scroll.
+ if (mIsRtl) {
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
+ }
+ if (mIsRtl) {
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
}
return super.computeMaxScrollX();
}
@@ -1733,7 +1795,7 @@
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage);
+ getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
index e425088..853a1c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,17 +16,34 @@
package com.android.launcher3.uioverrides;
+import android.content.Context;
import android.provider.DeviceConfig;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
+ public static final String NAMESPACE_LAUNCHER = "launcher";
+ public static final String TAG = "TogglableFlag";
public TogglableFlag(String key, boolean defaultValue, String description) {
super(key, defaultValue, description);
}
@Override
- public boolean getInitialValue(boolean value) {
- return DeviceConfig.getBoolean("launcher", getKey(), value);
+ public boolean getOverridenDefaultValue(boolean value) {
+ return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
+ }
+
+ @Override
+ public void addChangeListener(Context context, Runnable r) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_LAUNCHER,
+ context.getMainExecutor(),
+ (properties) -> {
+ if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
+ return;
+ }
+ initialize(context);
+ r.run();
+ });
}
}
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d942e9e..0396df6 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ስራ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"መተግበሪያ አልተጫነም።"</string>
<string name="activity_not_available" msgid="7456344436509528827">"መተግበሪያ አይገኝም"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 5528a19..7c1ce84 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Tətbiq quraşdırılmayıb."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 5650457..65ad91e 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 50f92da..4ef9ec3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Feina"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'aplicació no s\'ha instal·lat."</string>
<string name="activity_not_available" msgid="7456344436509528827">"L\'aplicació no està disponible."</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 2363e61..d80e905 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Εργασία"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Η εφαρμογή δεν έχει εγκατασταθεί."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 9ba46c1..10aebe7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Lana"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikazioa instalatu gabe dago."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ez dago erabilgarri aplikazioa"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 7b4bcb1..65db47e 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Android Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
@@ -136,7 +135,7 @@
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Retrouvez ici vos applications professionnelles"</string>
<string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Les applications professionnelles sont accompagnées d\'un badge et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement."</string>
<string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
- <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications et applications désactivées"</string>
<string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
<string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index e115a72..ca5ba3c 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Traballo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"A aplicación non está instalada"</string>
<string name="activity_not_available" msgid="7456344436509528827">"A aplicación non está dispoñible"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 7def3ac..3228dea 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 1caa27f..b738bc0 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 211f735..92c7acc 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kantor"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikasi tidak dipasang."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikasi tidak tersedia"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 14c8300..67ea6a8 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ಕೆಲಸ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
<string name="activity_not_available" msgid="7456344436509528827">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 9550a5f..ab14e89 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Стартер3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Работа"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Апликацијата не е инсталирана."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Апликацијата не е достапна"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index fd1f7d9..78856f6 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"ဖွင့်တင်စက်၃"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"အလုပ်"</string>
<string name="activity_not_found" msgid="8071924732094499514">"အက်ပ်မထည့်သွင်းထားပါ"</string>
<string name="activity_not_available" msgid="7456344436509528827">"အက်ပ်လက်လှမ်း မမှီပါ"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index e88ff18..bb4834f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Tela de início 3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"O app não está instalado."</string>
<string name="activity_not_available" msgid="7456344436509528827">"O app não está disponível"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1251d7e..f8a61f1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplicația nu este instalată."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplicația nu este disponibilă"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 340a128..5bbf7c3 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Pracovné"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikácia nie je nainštalovaná."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikácia nie je k dispozícii"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6f33c4f..445e382 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kazini"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programu haijasakinishwa."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programu haipatikani"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index c926bc1..6e933a1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"பணியிடம்"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ஆப்ஸ் இல்லை"</string>
@@ -36,7 +35,7 @@
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்"</string>
<string name="place_automatically" msgid="8064208734425456485">"தானாகவே சேர்"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"பயன்பாடுகளில் தேடுக"</string>
- <string name="all_apps_loading_message" msgid="5813968043155271636">"பயன்பாடுகளை ஏற்றுகிறது…"</string>
+ <string name="all_apps_loading_message" msgid="5813968043155271636">"ஆப்ஸை ஏற்றுகிறது…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
<string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
@@ -45,7 +44,7 @@
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
<string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"பயன்பாடுகளின் பட்டியல்"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ஆப்ஸின் பட்டியல்"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"தனிப்பட்ட ஆப்ஸ் பட்டியல்"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"பணி ஆப்ஸ் பட்டியல்"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 03c02ad..5ab9b86 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"งาน"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ไม่ได้ติดตั้งแอป"</string>
<string name="activity_not_available" msgid="7456344436509528827">"แอปไม่พร้อมใช้งาน"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 83178fe..b937764 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Isiqalisi3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Umsebenzi"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Uhlelo lokusebenza alufakiwe."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uhlelo lokusebenza alutholakali"</string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 61d05c1..d667e8c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -158,6 +158,9 @@
import java.util.List;
import java.util.function.Predicate;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
/**
* Default launcher application.
*/
@@ -209,10 +212,9 @@
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
// How long to wait before the new-shortcut animation automatically pans the workspace
- private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+ @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
- @Thunk
- static final int NEW_APPS_ANIMATION_DELAY = 500;
+ @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
@@ -332,8 +334,8 @@
UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
-
- mAppWidgetHost = new LauncherAppWidgetHost(this);
+ mAppWidgetHost = new LauncherAppWidgetHost(this,
+ appWidgetId -> getWorkspace().removeWidget(appWidgetId));
mAppWidgetHost.startListening();
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2a801d6..b4a2216 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -94,6 +94,7 @@
if (FeatureFlags.IS_DOGFOOD_BUILD) {
filter.addAction(ACTION_FORCE_ROLOAD);
}
+ FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index ffdd6b7..80c5c3e 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -35,6 +35,7 @@
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.util.ArrayList;
+import java.util.function.IntConsumer;
/**
@@ -56,9 +57,17 @@
private final Context mContext;
private int mFlags = FLAG_RESUMED;
+ private IntConsumer mAppWidgetRemovedCallback = null;
+
public LauncherAppWidgetHost(Context context) {
+ this(context, null);
+ }
+
+ public LauncherAppWidgetHost(Context context,
+ IntConsumer appWidgetRemovedCallback) {
super(context, APPWIDGET_HOST_ID);
mContext = context;
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback;
}
@Override
@@ -211,7 +220,7 @@
}
view.setAppWidget(appWidgetId, appWidget);
view.switchToErrorView();
- return view;
+ return view;
}
}
}
@@ -229,6 +238,15 @@
info.initSpans(mContext);
}
+ //TODO: make this override when SDK is updated
+ //@Override
+ protected void onAppWidgetRemoved(int appWidgetId) {
+ if (mAppWidgetRemovedCallback == null) {
+ return;
+ }
+ mAppWidgetRemovedCallback.accept(appWidgetId);
+ }
+
@Override
public void deleteAppWidgetId(int appWidgetId) {
super.deleteAppWidgetId(appWidgetId);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4ee0618..14f6598 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -53,6 +54,10 @@
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
@@ -171,6 +176,30 @@
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
+ public void onSessionFailure(String packageName, UserHandle user) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
+ synchronized (dataModel) {
+ for (ItemInfo info : dataModel.itemsIdMap) {
+ if (info instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) info).hasPromiseIconUi()
+ && user.equals(info.user)
+ && info.getIntent() != null
+ && TextUtils.equals(packageName, info.getIntent().getPackage())) {
+ removedIds.put(info.id, true /* remove */);
+ }
+ }
+ }
+
+ if (!removedIds.isEmpty()) {
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+ }
+ }
+ });
+ }
+
@Override
public void onPackageRemoved(String packageName, UserHandle user) {
onPackagesRemoved(user, packageName);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 56a8966..f9201d0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2802,6 +2802,23 @@
}
/**
+ * Removed widget from workspace by appWidgetId
+ * @param appWidgetId
+ */
+ public void removeWidget(int appWidgetId) {
+ mapOverItems((info, view) -> {
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo appWidgetInfo = (LauncherAppWidgetInfo) info;
+ if (appWidgetInfo.appWidgetId == appWidgetId) {
+ mLauncher.removeItem(view, appWidgetInfo, true);
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ /**
* Removes all folder listeners
*/
public void removeFolderListeners() {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0c1303b..0c12c60 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,5 +1,7 @@
package com.android.launcher3.accessibility;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
import static com.android.launcher3.LauncherState.NORMAL;
import android.app.AlertDialog;
@@ -30,14 +32,14 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ShortcutUtil;
@@ -164,6 +166,13 @@
}
public boolean performAction(final View host, final ItemInfo item, int action) {
+ if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) {
+ CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
+ if (popup.canShow()) {
+ popup.show();
+ return true;
+ }
+ }
if (action == MOVE) {
beginAccessibleDrag(host, item);
} else if (action == ADD_TO_WORKSPACE) {
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
new file mode 100644
index 0000000..0f34c1e
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2019 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.anim;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.FloatProperty;
+
+import com.android.launcher3.util.DefaultDisplay;
+
+import androidx.annotation.FloatRange;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Utility class to build an object animator which follows the same path as a spring animation for
+ * an underdamped spring.
+ */
+public class SpringAnimationBuilder<T> extends FloatProperty<T> {
+
+ private final T mTarget;
+ private final FloatProperty<T> mProperty;
+
+ private float mStartValue;
+ private float mEndValue;
+ private float mVelocity = 0;
+
+ private float mStiffness = SpringForce.STIFFNESS_MEDIUM;
+ private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ private float mMinVisibleChange = 1;
+
+ // Multiplier to the min visible change value for value threshold
+ private static final float THRESHOLD_MULTIPLIER = 0.65f;
+
+ /**
+ * The spring equation is given as
+ * x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t)
+ * v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t)
+ * + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2
+ *
+ * a = x(0)
+ * b = beta * x(0) / (2 * gamma) + v(0) / gamma
+ */
+ private double beta;
+ private double gamma;
+
+ private double a, b;
+ private double va, vb;
+
+ // Threshold for velocity and value to determine when it's reasonable to assume that the spring
+ // is approximately at rest.
+ private double mValueThreshold;
+ private double mVelocityThreshold;
+
+ private float mCurrentTime = 0;
+
+ public SpringAnimationBuilder(T target, FloatProperty<T> property) {
+ super("dynamic-spring-property");
+ mTarget = target;
+ mProperty = property;
+
+ mStartValue = mProperty.get(target);
+ }
+
+ public SpringAnimationBuilder<T> setEndValue(float value) {
+ mEndValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setStartValue(float value) {
+ mStartValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setValues(float... values) {
+ if (values.length > 1) {
+ mStartValue = values[0];
+ mEndValue = values[values.length - 1];
+ } else {
+ mEndValue = values[0];
+ }
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setStiffness(
+ @FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
+ if (stiffness <= 0) {
+ throw new IllegalArgumentException("Spring stiffness constant must be positive.");
+ }
+ mStiffness = stiffness;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setDampingRatio(
+ @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+ float dampingRatio) {
+ if (dampingRatio <= 0 || dampingRatio >= 1) {
+ throw new IllegalArgumentException("Damping ratio must be between 0 and 1");
+ }
+ mDampingRatio = dampingRatio;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setMinimumVisibleChange(
+ @FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
+ if (minimumVisibleChange <= 0) {
+ throw new IllegalArgumentException("Minimum visible change must be positive.");
+ }
+ mMinVisibleChange = minimumVisibleChange;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) {
+ mVelocity = startVelocity;
+ return this;
+ }
+
+ @Override
+ public void setValue(T object, float time) {
+ mCurrentTime = time;
+ mProperty.setValue(
+ object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
+ }
+
+ @Override
+ public Float get(T t) {
+ return mCurrentTime;
+ }
+
+ public ObjectAnimator build(Context context) {
+ int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
+ double naturalFreq = Math.sqrt(mStiffness);
+ double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
+
+ // All the calculations assume the stable position to be 0, shift the values accordingly.
+ beta = 2 * mDampingRatio * naturalFreq;
+ gamma = dampedFreq;
+ a = mStartValue - mEndValue;
+ b = beta * a / (2 * gamma) + mVelocity / gamma;
+
+ va = a * beta / 2 - b * gamma;
+ vb = a * gamma + beta * b / 2;
+
+ mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER;
+
+ // This multiplier is used to calculate the velocity threshold given a certain value
+ // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
+ // then the velocity is a reasonable threshold.
+ mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs;
+
+ // Find the duration (in seconds) for the spring to reach equilibrium.
+ // equilibrium is reached when x = 0
+ double duration = Math.atan2(-a, b) / gamma;
+
+ // Keep moving ahead until the velocity reaches equilibrium.
+ double piByG = Math.PI / gamma;
+ while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration))
+ >= mVelocityThreshold) {
+ duration += piByG;
+ }
+
+ // Find the shortest time
+ double edgeTime = Math.max(0, duration - piByG / 2);
+ double minDiff = singleFrameMs / 2000.0; // Half frame time in seconds
+
+ do {
+ if ((duration - edgeTime) < minDiff) {
+ break;
+ }
+ double mid = (edgeTime + duration) / 2;
+ if (isAtEquilibrium(mid)) {
+ duration = mid;
+ } else {
+ edgeTime = mid;
+ }
+ } while (true);
+
+
+ long durationMs = (long) (1000.0 * duration);
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
+ animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
+ animator.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mProperty.setValue(mTarget, mEndValue);
+ }
+ });
+ return animator;
+ }
+
+ private boolean isAtEquilibrium(double t) {
+ double ec = exponentialComponent(t);
+
+ if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) {
+ return false;
+ }
+ return Math.abs(ec * cosSinV(t)) < mVelocityThreshold;
+ }
+
+ private double exponentialComponent(double t) {
+ return Math.pow(Math.E, - beta * t / 2);
+ }
+
+ private double cosSinX(double t) {
+ return cosSin(t, a, b);
+ }
+
+ private double cosSinV(double t) {
+ return cosSin(t, va, vb);
+ }
+
+ private double cosSin(double t, double cosFactor, double sinFactor) {
+ double angle = t * gamma;
+ return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle);
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index c423d13..2e22cba 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -139,15 +139,20 @@
* Add a promise app icon to the workspace iff:
* - The settings for it are enabled
* - The user installed the app
- * - There is a provided app icon (For apps with no launching activity, no icon is provided).
+ * - There is an app icon and label (For apps with no launching activity, no icon is provided).
+ * - The app is not already installed
+ * - A promise icon for the session has not already been created
*/
private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
- && sessionInfo != null
+ && verify(sessionInfo) != null
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())) {
+ && !TextUtils.isEmpty(sessionInfo.getAppLabel())
+ && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
+ getUserHandle(sessionInfo)) == null) {
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
mPromiseIconIds.add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
@@ -182,12 +187,14 @@
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
packageName, key.mUser));
- if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
+ if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ && mPromiseIconIds.contains(sessionId)) {
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (appState != null) {
- LauncherModel model = appState.getModel();
- model.onPackageRemoved(packageName, key.mUser);
+ appState.getModel().onSessionFailure(packageName, key.mUser);
}
+ // If it is successful, the id is removed in the the package added flow.
+ removePromiseIconId(sessionId);
}
}
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1523278..9455889 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -147,6 +147,8 @@
public static abstract class BaseTogglableFlag {
private final String key;
+ // should be value that is hardcoded in client side.
+ // Comparatively, getDefaultValue() can be overridden.
private final boolean defaultValue;
private final String description;
private boolean currentValue;
@@ -156,8 +158,9 @@
boolean defaultValue,
String description) {
this.key = checkNotNull(key);
- this.currentValue = this.defaultValue = getInitialValue(defaultValue);
+ this.currentValue = this.defaultValue = defaultValue;
this.description = checkNotNull(description);
+
synchronized (sLock) {
sFlags.add((TogglableFlag)this);
}
@@ -173,16 +176,18 @@
return key;
}
- void initialize(Context context) {
- currentValue = getFromStorage(context, defaultValue);
+ protected void initialize(Context context) {
+ currentValue = getFromStorage(context, getDefaultValue());
}
- protected abstract boolean getInitialValue(boolean value);
+ protected abstract boolean getOverridenDefaultValue(boolean value);
+
+ protected abstract void addChangeListener(Context context, Runnable r);
public void updateStorage(Context context, boolean value) {
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
Context.MODE_PRIVATE).edit();
- if (value == defaultValue) {
+ if (value == getDefaultValue()) {
editor.remove(key).apply();
} else {
editor.putBoolean(key, value).apply();
@@ -191,11 +196,11 @@
boolean getFromStorage(Context context, boolean defaultValue) {
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
- .getBoolean(key, defaultValue);
+ .getBoolean(key, getDefaultValue());
}
boolean getDefaultValue() {
- return defaultValue;
+ return getOverridenDefaultValue(defaultValue);
}
/** Returns the value of the flag at process start, including any overrides present. */
@@ -212,6 +217,8 @@
return "TogglableFlag{"
+ "key=" + key + ", "
+ "defaultValue=" + defaultValue + ", "
+ + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
+ + "currentValue=" + currentValue + ", "
+ "description=" + description
+ "}";
}
@@ -224,7 +231,7 @@
if (o instanceof TogglableFlag) {
BaseTogglableFlag that = (BaseTogglableFlag) o;
return (this.key.equals(that.getKey()))
- && (this.defaultValue == that.getDefaultValue())
+ && (this.getDefaultValue() == that.getDefaultValue())
&& (this.description.equals(that.getDescription()));
}
return false;
@@ -236,7 +243,7 @@
h$ *= 1000003;
h$ ^= key.hashCode();
h$ *= 1000003;
- h$ ^= defaultValue ? 1231 : 1237;
+ h$ ^= getDefaultValue() ? 1231 : 1237;
h$ *= 1000003;
h$ ^= description.hashCode();
return h$;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 5fb833c..a8ff232 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -43,6 +43,7 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
@@ -236,7 +237,8 @@
@Override
protected String getIconSystemState(String packageName) {
- return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
+ return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
+ + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
}
public static abstract class IconLoadRequest extends HandlerRunnable {
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 47a0f53..923a89b 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -8,8 +8,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -32,8 +30,7 @@
*/
public final class FileLog {
- protected static final boolean ENABLED =
- FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE;
+ protected static final boolean ENABLED = true;
private static final String FILE_NAME_PREFIX = "log-";
private static final DateFormat DATE_FORMAT =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 792d69f..af99713 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -23,37 +23,57 @@
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
- public static boolean supportsShortcuts(ItemInfo info) {
- return isActive(info) && (isApp(info) || isPinnedShortcut(info));
- }
+ /**
+ * Returns true when we should show shortcut menu for the item.
+ */
+ public static boolean supportsShortcuts(ItemInfo info) {
+ return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+ }
- public static boolean supportsDeepShortcuts(ItemInfo info) {
- return isActive(info) && isApp(info);
- }
+ /**
+ * Returns true when we should show depp shortcuts in shortcut menu for the item.
+ */
+ public static boolean supportsDeepShortcuts(ItemInfo info) {
+ return isActive(info) && isApp(info);
+ }
- public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
- return isActive(info) && isPinnedShortcut(info) ?
- ShortcutKey.fromItemInfo(info).getId() : null;
- }
+ /**
+ * Returns the shortcut id if the item is a pinned shortcut.
+ */
+ public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
+ return isActive(info) && isPinnedShortcut(info)
+ ? ShortcutKey.fromItemInfo(info).getId() : null;
+ }
- public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
- return isActive(info) && isPinnedShortcut(info) ?
- ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
- }
+ /**
+ * Returns the person keys associated with the item. (Has no function right now.)
+ */
+ public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
+ return isActive(info) && isPinnedShortcut(info)
+ ? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
+ }
- private static boolean isActive(ItemInfo info) {
- boolean isLoading = info instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) info).hasPromiseIconUi();
- return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
- }
+ /**
+ * Returns true if the item is a deep shortcut.
+ */
+ public static boolean isDeepShortcut(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && info instanceof WorkspaceItemInfo;
+ }
- private static boolean isApp(ItemInfo info) {
- return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- }
+ private static boolean isActive(ItemInfo info) {
+ boolean isLoading = info instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+ return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+ }
- private static boolean isPinnedShortcut(ItemInfo info) {
- return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && info.container != ItemInfo.NO_ID
- && info instanceof WorkspaceItemInfo;
- }
+ private static boolean isApp(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ }
+
+ private static boolean isPinnedShortcut(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && info.container != ItemInfo.NO_ID
+ && info instanceof WorkspaceItemInfo;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 3145893..3912b44 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -720,7 +720,7 @@
*/
@UiThread
public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
- IconLoadResult result = new IconLoadResult();
+ IconLoadResult result = new IconLoadResult(info);
MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
RectF position = new RectF();
getLocationBoundsForView(l, v, isOpening, position);
@@ -749,10 +749,13 @@
// Get the drawable on the background thread
boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
- view.mIconLoadResult = sIconLoadResult;
- if (shouldLoadIcon && view.mIconLoadResult == null) {
- view.mIconLoadResult = fetchIcon(launcher, originalView,
- (ItemInfo) originalView.getTag(), isOpening);
+ if (shouldLoadIcon) {
+ if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) {
+ view.mIconLoadResult = sIconLoadResult;
+ } else {
+ view.mIconLoadResult = fetchIcon(launcher, originalView,
+ (ItemInfo) originalView.getTag(), isOpening);
+ }
}
sIconLoadResult = null;
@@ -894,10 +897,15 @@
}
private static class IconLoadResult {
+ final ItemInfo itemInfo;
Drawable drawable;
Drawable badge;
int iconOffset;
Runnable onIconLoaded;
boolean isIconLoaded;
+
+ public IconLoadResult(ItemInfo itemInfo) {
+ this.itemInfo = itemInfo;
+ }
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
index e875a3c..60f12d8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides;
+import android.content.Context;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
@@ -25,7 +26,10 @@
}
@Override
- public boolean getInitialValue(boolean value) {
+ public boolean getOverridenDefaultValue(boolean value) {
return value;
}
+
+ @Override
+ public void addChangeListener(Context context, Runnable r) { }
}
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
new file mode 100644
index 0000000..efbd9c9
--- /dev/null
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 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.compat;
+
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.UUID;
+
+
+/**
+ * Test to verify promise icon flow.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class PromiseIconUiTest extends AbstractLauncherUiTest {
+
+ private int mSessionId = -1;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ mSessionId = -1;
+ }
+
+ @After
+ public void tearDown() {
+ if (mSessionId > -1) {
+ mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ }
+ }
+
+ /**
+ * Create a session and return the id.
+ */
+ private int createSession(String label, Bitmap icon) throws Throwable {
+ SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ params.setAppPackageName("test.promise.app");
+ params.setAppLabel(label);
+ params.setAppIcon(icon);
+ params.setInstallReason(PackageManager.INSTALL_REASON_USER);
+ return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
+ }
+
+ @Test
+ public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
+ final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+ final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ info != null && TextUtils.equals(info.title, appLabel);
+
+ // Create and add test session
+ mSessionId = createSession(appLabel, Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
+
+ // Verify promise icon is added
+ waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
+
+ // Remove session
+ mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ mSessionId = -1;
+
+ // Verify promise icon is removed
+ waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+ }
+
+ @Test
+ public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
+ final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+ final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ info != null && TextUtils.equals(info.title, appLabel);
+
+ // Create and add test session without icon or label
+ mSessionId = createSession(null, null);
+
+ // Sleep for duration of animation if a view was to be added + some buffer time.
+ Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500);
+
+ // Verify promise icon is not added
+ waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 5c38c8d..3f35a3a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,6 +41,7 @@
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -69,12 +70,14 @@
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testWidgetConfig() throws Throwable {
runTest(true);
}
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testConfigCancelled() throws Throwable {
runTest(false);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index b9abc2e..1edce22 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.ui.widget;
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import androidx.test.filters.LargeTest;
@@ -22,10 +25,12 @@
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.tapl.Widget;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +46,7 @@
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
@@ -58,5 +64,10 @@
(info, view) -> info instanceof LauncherAppWidgetInfo &&
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
widgetInfo.provider.getClassName())).call());
+
+ final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
+ DEFAULT_UI_TIMEOUT);
+ assertNotNull("Widget not found on the workspace", widget);
+ widget.launch(getAppPackageName());
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 14f2c85..c6e7dac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -368,7 +368,7 @@
}
}
- private void assertEquals(String message, String expected, String actual) {
+ void assertEquals(String message, String expected, String actual) {
if (!TextUtils.equals(expected, actual)) {
fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index f3295d0..7d308af 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,7 +77,7 @@
return LauncherInstrumentation.ContainerType.WIDGETS;
}
- public Widget getWidget(String label) {
+ public Widget getWidget(String labelText) {
final int margin = ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
final UiObject2 widgetsContainer = verifyActiveContainer();
@@ -86,17 +86,24 @@
final Point displaySize = mLauncher.getRealDisplaySize();
int i = 0;
- final BySelector selector = By.
- clazz("com.android.launcher3.widget.WidgetCell").
- hasDescendant(By.text(label));
+ final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
for (; ; ) {
- final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
- if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
- return new Widget(mLauncher, widget);
+ final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
+ if (label != null) {
+ final UiObject2 widget = label.getParent().getParent();
+ mLauncher.assertEquals(
+ "View is not WidgetCell",
+ "com.android.launcher3.widget.WidgetCell",
+ widget.getClassName());
+
+ if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+ return new Widget(mLauncher, widget);
+ }
}
+
if (++i > 40) fail("Too many attempts");
- mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, 50);
+ mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
}
}
}