Merge "Revert "Revert "Temporarily make TouchInteractionService direct boot unaware""" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index afc8e47..47e0e61 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -19,14 +19,12 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import android.graphics.Rect;
-
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.IconRecentsView;
/**
* Definition for overview state
@@ -50,6 +48,12 @@
}
@Override
+ public void onStateEnabled(Launcher launcher) {
+ IconRecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.onBeginTransitionToOverview();
+ }
+
+ @Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
return new PageAlphaProvider(DEACCEL_2) {
@Override
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 12f5360..d7cba39 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -35,7 +35,8 @@
* Provides recents-related {@link UiFactory} logic and classes.
*/
public abstract class RecentsUiFactory {
-
+
+ public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 77c3f33..57cd60a 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -33,10 +33,10 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
- private final ArrayList<Task> mTaskList;
+ private final TaskListLoader mLoader;
- public TaskAdapter(@NonNull ArrayList<Task> taskList) {
- mTaskList = taskList;
+ public TaskAdapter(@NonNull TaskListLoader loader) {
+ mLoader = loader;
}
@Override
@@ -48,11 +48,16 @@
@Override
public void onBindViewHolder(TaskHolder holder, int position) {
- holder.bindTask(mTaskList.get(position));
+ ArrayList<Task> tasks = mLoader.getCurrentTaskList();
+ if (position >= tasks.size()) {
+ // Task list has updated.
+ return;
+ }
+ holder.bindTask(tasks.get(position));
}
@Override
public int getItemCount() {
- return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY);
+ return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
}
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
new file mode 100644
index 0000000..9f359b4
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -0,0 +1,118 @@
+/*
+ * 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.quickstep;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+/**
+ * This class is responsible for maintaining the list of tasks and the task content. The list must
+ * be updated explicitly with {@link #loadTaskList} whenever the list needs to be
+ * up-to-date.
+ */
+public final class TaskListLoader {
+
+ private final RecentsModel mRecentsModel;
+
+ private ArrayList<Task> mTaskList = new ArrayList<>();
+ private int mTaskListChangeId;
+
+ public TaskListLoader(Context context) {
+ mRecentsModel = RecentsModel.INSTANCE.get(context);
+ }
+
+ /**
+ * Returns the current task list as of the last completed load (see
+ * {@link #loadTaskList}). This list of tasks is guaranteed to always have all its task
+ * content loaded.
+ *
+ * @return the current list of tasks w/ all content loaded
+ */
+ public ArrayList<Task> getCurrentTaskList() {
+ return mTaskList;
+ }
+
+ /**
+ * Fetches the most recent tasks and updates the task list asynchronously. In addition it
+ * loads the content for each task (icon and label). The callback and task list being updated
+ * only occur when all task content is fully loaded and up-to-date.
+ *
+ * @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
+ * thread
+ */
+ public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
+ if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+ // Current task list is already up to date. No need to update.
+ if (onTasksLoadedCallback != null) {
+ onTasksLoadedCallback.accept(mTaskList);
+ }
+ return;
+ }
+ // TODO: Look into error checking / more robust handling for when things go wrong.
+ mTaskListChangeId = mRecentsModel.getTasks(tasks -> {
+ // Reverse tasks to put most recent at the bottom of the view
+ Collections.reverse(tasks);
+ // Load task content
+ loadTaskContents(tasks, () -> {
+ mTaskList = tasks;
+ if (onTasksLoadedCallback != null) {
+ onTasksLoadedCallback.accept(mTaskList);
+ }
+ });
+ });
+ }
+
+ /**
+ * Loads task content for a list of tasks, including the label and the icon. Uses the list of
+ * tasks since the last load as a cache for loaded content.
+ *
+ * @param tasksToLoad list of tasks that need to load their content
+ * @param onLoadedCallback runnable to run after all tasks have loaded their content
+ */
+ private void loadTaskContents(ArrayList<Task> tasksToLoad,
+ @Nullable Runnable onLoadedCallback) {
+ AtomicInteger loadRequestsCount = new AtomicInteger(0);
+ for (Task task : tasksToLoad) {
+ int index = mTaskList.indexOf(task);
+ if (index >= 0) {
+ // If we've already loaded the task and have its content then just copy it over.
+ Task loadedTask = mTaskList.get(index);
+ task.titleDescription = loadedTask.titleDescription;
+ task.icon = loadedTask.icon;
+ } else {
+ // Otherwise, load the content in the background.
+ loadRequestsCount.getAndIncrement();
+ mRecentsModel.getIconCache().updateIconInBackground(task, loadedTask -> {
+ if (loadRequestsCount.decrementAndGet() == 0 && onLoadedCallback != null) {
+ onLoadedCallback.run();
+ }
+ });
+ }
+ }
+ if (loadRequestsCount.get() == 0 && onLoadedCallback != null) {
+ onLoadedCallback.run();
+ }
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2858deb..89a8454 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -52,6 +52,7 @@
ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+ mRecentsModel.onInitializeSystemUI(bundle);
}
@Override
@@ -77,6 +78,11 @@
mOverviewCommandHelper.onTip(actionType, viewType);
}
+ @Override
+ public void onAssistantAvailable(boolean available) {
+ // TODO handle assistant
+ }
+
/** Deprecated methods **/
public void onQuickStep(MotionEvent motionEvent) { }
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 15da10c..afb0540 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,11 +27,8 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.R;
-import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskAdapter;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
+import com.android.quickstep.TaskListLoader;
/**
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -77,13 +74,12 @@
*/
@ViewDebug.ExportedProperty(category = "launcher")
- // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter.
- private static final ArrayList<Task> DUMMY_TASK_LIST = new ArrayList<>();
private final Context mContext;
private float mTranslationYFactor;
private TaskAdapter mTaskAdapter;
private RecyclerView mTaskRecyclerView;
+ private TaskListLoader mTaskLoader;
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -93,13 +89,30 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST);
+ mTaskLoader = new TaskListLoader(mContext);
+ mTaskAdapter = new TaskAdapter(mTaskLoader);
mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
mTaskRecyclerView.setAdapter(mTaskAdapter);
mTaskRecyclerView.setLayoutManager(
new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
}
+ /**
+ * Logic for when we know we are going to overview/recents and will be putting up the recents
+ * view. This should be used to prepare recents (e.g. load any task data, etc.) before it
+ * becomes visible.
+ *
+ * TODO: Hook this up for fallback recents activity as well
+ */
+ public void onBeginTransitionToOverview() {
+ // Load any task changes
+ mTaskLoader.loadTaskList(tasks -> {
+ // TODO: Put up some loading UI while task content is loading. May have to do something
+ // smarter when animating from app to overview.
+ mTaskAdapter.notifyDataSetChanged();
+ });
+ }
+
public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index 76abe8d..96b0a9e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -48,7 +48,7 @@
private ShadowGenerator mShadowGenerator;
private Drawable mWrapperIcon;
- private int mWrapperBackgroundColor;
+ private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
mContext = context.getApplicationContext();
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 8c58e3e..4b64ad4 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/recents_ui_overrides/res/layout/hint.xml b/quickstep/recents_ui_overrides/res/layout/hint.xml
deleted file mode 100644
index 7e2d6af..0000000
--- a/quickstep/recents_ui_overrides/res/layout/hint.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<com.android.quickstep.hints.HintView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/chip_hint_height"
- android:layout_gravity="center_horizontal|bottom"
- android:paddingStart="@dimen/chip_hint_start_padding"
- android:paddingEnd="@dimen/chip_hint_end_padding"
- android:background="@drawable/chip_hint_background_light"
- android:gravity="center"
- android:layout_marginHorizontal="@dimen/chip_hint_horizontal_margin"
- android:orientation="horizontal"
- android:elevation="@dimen/chip_hint_elevation"
- android:layoutDirection="ltr">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/chip_icon_size"
- android:layout_height="@dimen/chip_icon_size"
- android:visibility="gone"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:contentDescription="@null"/>
-
- <TextView
- android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/chip_text_height"
- android:paddingTop="@dimen/chip_text_top_padding"
- android:paddingStart="@dimen/chip_text_start_padding"
- android:fontFamily="google-sans-medium"
- android:textAlignment="textStart"
- android:singleLine="true"
- android:textColor="@color/chip_hint_foreground_color"
- android:textSize="@dimen/chip_text_size"
- android:ellipsize="none"
- android:includeFontPadding="true"/>
-
-
-</com.android.quickstep.hints.HintView>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/hint_container.xml b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
index 336f63e..f8723fc 100644
--- a/quickstep/recents_ui_overrides/res/layout/hint_container.xml
+++ b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
@@ -14,9 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.hints.HintsContainer
+<com.android.quickstep.hints.ChipsContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/chip_hint_height"
+ android:layout_gravity="bottom"
android:gravity="center_horizontal"
android:orientation="horizontal"/>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 027fd91..81ff0c7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -44,6 +44,7 @@
*/
public abstract class RecentsUiFactory {
+ public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
@@ -61,7 +62,6 @@
if (swipeUpToHome) {
list.add(new FlingAndHoldTouchController(launcher));
- list.add(new OverviewToAllAppsTouchController(launcher));
} else {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new OverviewToAllAppsTouchController(launcher));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
new file mode 100644
index 0000000..109a4c5
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -0,0 +1,200 @@
+/*
+ * 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.quickstep;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.NavigationBarCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.launcher3.R;
+
+/**
+ * Touch consumer for handling events to launch assistant from launcher
+ */
+public class AssistantTouchConsumer implements InputConsumer {
+ private static final String TAG = "AssistantTouchConsumer";
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private int mActivePointerId = -1;
+
+ private final int mDisplayRotation;
+ private final Rect mStableInsets = new Rect();
+
+ private final float mDragSlop;
+ private final float mTouchSlop;
+ private final float mThreshold;
+
+ private float mStartDisplacement;
+ private boolean mPassedDragSlop;
+ private boolean mPassedTouchSlop;
+ private long mPassedTouchSlopTime;
+ private boolean mLaunchedAssistant;
+ private float mLastProgress;
+
+ private final ISystemUiProxy mSysUiProxy;
+
+ public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy) {
+ mSysUiProxy = systemUiProxy;
+
+ mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
+ mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
+ mThreshold = context.getResources().getDimension(R.dimen.gestures_assistant_threshold);
+
+ Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
+ mDisplayRotation = display.getRotation();
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_ASSISTANT;
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ // TODO add logging
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastProgress = -1;
+ break;
+ }
+ case ACTION_POINTER_UP: {
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ }
+ case ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ float displacement = getDisplacement(ev);
+
+ if (!mPassedDragSlop) {
+ // Normal gesture, ensure we pass the drag slop before we start tracking
+ // the gesture
+ if (Math.abs(displacement) > mDragSlop) {
+ mPassedDragSlop = true;
+ mStartDisplacement = displacement;
+ mPassedTouchSlopTime = SystemClock.uptimeMillis();
+ }
+ }
+
+ if (!mPassedTouchSlop) {
+ if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
+ mTouchSlop) {
+ mPassedTouchSlop = true;
+ if (!mPassedDragSlop) {
+ mPassedDragSlop = true;
+ mStartDisplacement = displacement;
+ mPassedTouchSlopTime = SystemClock.uptimeMillis();
+ }
+ }
+ }
+
+ if (mPassedDragSlop) {
+ // Move
+ float distance = mStartDisplacement - displacement;
+ if (distance >= 0) {
+ onAssistantProgress(distance / mThreshold);
+ }
+ }
+ break;
+ }
+ case ACTION_CANCEL:
+ break;
+ case ACTION_UP: {
+ if (ev.getEventTime() - mPassedTouchSlopTime < ViewConfiguration.getTapTimeout()) {
+ onAssistantProgress(1);
+ }
+
+ break;
+ }
+ }
+ }
+
+ private void onAssistantProgress(float progress) {
+ if (mLastProgress == progress) {
+ return;
+ }
+ try {
+ mSysUiProxy.onAssistantProgress(Math.max(0, Math.min(1, progress)));
+ if (progress >= 1 && !mLaunchedAssistant) {
+ mSysUiProxy.startAssistant(new Bundle());
+ mLaunchedAssistant = true;
+ }
+ mLastProgress = progress;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify SysUI to start/send assistant progress: " + progress, e);
+ }
+ }
+
+ private boolean isNavBarOnRight() {
+ return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
+ }
+
+ private boolean isNavBarOnLeft() {
+ return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
+ }
+
+ private float getDisplacement(MotionEvent ev) {
+ float eventX = ev.getX();
+ float eventY = ev.getY();
+ float displacement = eventY - mDownPos.y;
+ if (isNavBarOnRight()) {
+ displacement = eventX - mDownPos.x;
+ } else if (isNavBarOnLeft()) {
+ displacement = mDownPos.x - eventX;
+ }
+ return displacement;
+ }
+
+ static boolean withinTouchRegion(Context context, float x) {
+ return x > context.getResources().getDisplayMetrics().widthPixels
+ - context.getResources().getDimension(R.dimen.gestures_assistant_width);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
index 047f34c..d919a3e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
@@ -31,6 +31,11 @@
}
@Override
+ public int getType() {
+ return TYPE_DEVICE_LOCKED;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
// For now, just start the home intent so user is prompted to unlock the device.
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index 8dfb9ab..ad9fe78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -23,7 +23,16 @@
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {
- InputConsumer NO_OP = new InputConsumer() { };
+
+ int TYPE_NO_OP = 0;
+ int TYPE_OVERVIEW = 1;
+ int TYPE_OTHER_ACTIVITY = 2;
+ int TYPE_ASSISTANT = 3;
+ int TYPE_DEVICE_LOCKED = 4;
+
+ InputConsumer NO_OP = () -> TYPE_NO_OP;
+
+ int getType();
default boolean isActive() {
return false;
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 ffd3b4b..971987a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -29,11 +29,11 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.UserHandle;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -100,17 +100,20 @@
public HomeAnimationFactory prepareHomeUI(Launcher activity) {
final DeviceProfile dp = activity.getDeviceProfile();
final RecentsView recentsView = activity.getOverviewPanel();
- final ComponentName component = recentsView.getRunningTaskView().getTask().key
- .sourceComponent;
-
- final View workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
- final FloatingIconView floatingView = workspaceView == null ? null
- : new FloatingIconView(activity);
- final Rect iconLocation = new Rect();
- if (floatingView != null) {
- floatingView.matchPositionOf(activity, workspaceView, true /* hideOriginal */,
- iconLocation);
+ final TaskView runningTaskView = recentsView.getRunningTaskView();
+ final View workspaceView;
+ if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
+ workspaceView = activity.getWorkspace().getFirstMatchForAppClose(
+ runningTaskView.getTask().key.getComponent().getPackageName(),
+ UserHandle.of(runningTaskView.getTask().key.userId));
+ } else {
+ workspaceView = null;
}
+ final Rect iconLocation = new Rect();
+ final FloatingIconView floatingView = workspaceView == null ? null
+ : FloatingIconView.getFloatingIconView(activity, workspaceView,
+ true /* hideOriginal */, false /* useDrawableAsIs */,
+ activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null);
return new HomeAnimationFactory() {
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 67bfeaa..e3afb92 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -148,6 +148,11 @@
}
@Override
+ public int getType() {
+ return TYPE_OTHER_ACTIVITY;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
return;
@@ -225,7 +230,7 @@
mTouchSlop) {
mPassedTouchSlop = true;
- TOUCH_INTERACTION_LOG.startQuickStep();
+ TOUCH_INTERACTION_LOG.addLog("startQuickstep");
if (mIsDeferredDownTarget) {
// Deferred gesture, start the animation and gesture tracking once
// we pass the actual touch slop
@@ -288,7 +293,7 @@
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
- TOUCH_INTERACTION_LOG.startRecentsAnimation();
+ TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
index 28c4db4..cce5cb3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -60,6 +60,11 @@
}
@Override
+ public int getType() {
+ return TYPE_OVERVIEW;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (mInvalidated) {
return;
@@ -120,7 +125,7 @@
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- TOUCH_INTERACTION_LOG.startQuickStep();
+ TOUCH_INTERACTION_LOG.addLog("startQuickstep");
}
mTrackingStarted = true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
deleted file mode 100644
index 4b660d4..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.view.MotionEvent;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.LinkedList;
-
-/**
- * Keeps track of debugging logs for a particular quickstep gesture.
- */
-public class TouchInteractionLog {
-
- // The number of gestures to log
- private static final int MAX_NUM_LOG_GESTURES = 5;
-
- private final Calendar mCalendar = Calendar.getInstance();
- private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MMM dd - kk:mm:ss:SSS");
- private final LinkedList<ArrayList<String>> mGestureLogs = new LinkedList<>();
-
- public void prepareForNewGesture() {
- mGestureLogs.add(new ArrayList<>());
- while (mGestureLogs.size() > MAX_NUM_LOG_GESTURES) {
- mGestureLogs.pop();
- }
- getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]");
- }
-
- public void setInputConsumer(InputConsumer consumer) {
- getCurrentLog().add("tc=" + consumer.getClass().getSimpleName());
- }
-
- public void addMotionEvent(MotionEvent event) {
- getCurrentLog().add("ev=" + event.getActionMasked());
- }
-
- public void startQuickStep() {
- getCurrentLog().add("qstStart");
- }
-
- public void startRecentsAnimation() {
- getCurrentLog().add("raStart");
- }
-
- public void startRecentsAnimationCallback(int numTargets) {
- getCurrentLog().add("raStartCb=" + numTargets);
- }
-
- public void cancelRecentsAnimation() {
- getCurrentLog().add("raCancel");
- }
-
- public void finishRecentsAnimation(boolean toHome) {
- getCurrentLog().add("raFinish=" + toHome);
- }
-
- public void dump(PrintWriter pw) {
- pw.println("TouchInteractionLog {");
- for (ArrayList<String> gesture : mGestureLogs) {
- pw.print(" ");
- for (String log : gesture) {
- pw.print(log + " ");
- }
- pw.println();
- }
- pw.println("}");
- }
-
- private ArrayList<String> getCurrentLog() {
- return mGestureLogs.getLast();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index cf50fc1..6d608ee 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -44,6 +44,8 @@
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -69,7 +71,8 @@
public static final LooperExecutor BACKGROUND_EXECUTOR =
new LooperExecutor(UiThreadHelper.getBackgroundLooper());
- public static final TouchInteractionLog TOUCH_INTERACTION_LOG = new TouchInteractionLog();
+ public static final EventLogArray TOUCH_INTERACTION_LOG =
+ new EventLogArray("touch_interaction_log", 40);
public static final int EDGE_NAV_BAR = 1 << 8;
@@ -86,6 +89,7 @@
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
runWhenUserUnlocked(() -> {
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+ mRecentsModel.onInitializeSystemUI(bundle);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
});
@@ -118,6 +122,11 @@
mOverviewCommandHelper.onTip(actionType, viewType);
}
+ @Override
+ public void onAssistantAvailable(boolean available) {
+ mAssistantAvailable = available;
+ }
+
/** Deprecated methods **/
public void onQuickStep(MotionEvent motionEvent) { }
@@ -171,6 +180,7 @@
private TaskOverlayFactory mTaskOverlayFactory;
private InputConsumerController mInputConsumer;
private SwipeSharedState mSwipeSharedState;
+ private boolean mAssistantAvailable;
private boolean mIsUserUnlocked;
private List<Runnable> mOnUserUnlockedCallbacks;
@@ -280,15 +290,13 @@
return;
}
MotionEvent event = (MotionEvent) ev;
+ TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
if (event.getAction() == ACTION_DOWN) {
- TOUCH_INTERACTION_LOG.prepareForNewGesture();
boolean useSharedState = mConsumer.isActive();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(useSharedState, event);
- TOUCH_INTERACTION_LOG.setInputConsumer(mConsumer);
+ TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
}
- TOUCH_INTERACTION_LOG.addMotionEvent(event);
-
mConsumer.onMotionEvent(event);
}
@@ -307,6 +315,10 @@
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
+ } else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
+ && FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
+ && AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
+ return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
} else if (mSwipeSharedState.goingToLauncher ||
mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
return OverviewInputConsumer.newInstance(
@@ -337,6 +349,6 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- TOUCH_INTERACTION_LOG.dump(pw);
+ TOUCH_INTERACTION_LOG.dump("", pw);
}
}
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 4d0136e..7dc58a5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -671,7 +671,7 @@
initTransitionEndpoints(dp);
mRecentsAnimationWrapper.setController(targetSet);
- TOUCH_INTERACTION_LOG.startRecentsAnimationCallback(targetSet.apps.length);
+ TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
@@ -682,7 +682,7 @@
mRecentsAnimationWrapper.setController(null);
mActivityInitListener.unregister();
setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
- TOUCH_INTERACTION_LOG.cancelRecentsAnimation();
+ TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
}
@UiThread
@@ -972,27 +972,36 @@
final RectF currentRect = new RectF();
final View floatingView = homeAnimationFactory.getFloatingView();
+ final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
+
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- if (floatingView instanceof FloatingIconView) {
+ if (isFloatingIconView) {
anim.addListener((FloatingIconView) floatingView);
}
+
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f;
anim.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
- float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
+ float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress);
// Initially go towards original target (task view in recents),
// but accelerate towards the final target.
// TODO: This is technically not correct. Instead, motion should continue at
// the released velocity but accelerate towards the target.
targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
originalTarget, finalTarget));
- currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
- float alpha = 1 - interpolatedProgress;
- mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
+ currentRect.set(rectFEvaluator.evaluate(interpolatedProgress, startRect, targetRect));
+
+ float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
+ windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
+ mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
.setSyncTransactionApplier(mSyncTransactionApplier);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
- if (floatingView instanceof FloatingIconView) {
- ((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
+ if (isFloatingIconView) {
+ ((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress,
+ windowAlphaThreshold);
}
});
anim.addListener(new AnimationSuccessListener() {
@@ -1016,7 +1025,7 @@
@UiThread
private void resumeLastTask() {
mRecentsAnimationWrapper.finish(false /* toRecents */, null);
- TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
+ TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
}
@UiThread
@@ -1034,7 +1043,7 @@
mMainThreadHandler);
});
}
- TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
+ TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
doLogGesture(NEW_TASK);
}
@@ -1143,7 +1152,7 @@
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
}
- TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
+ TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
@@ -1151,7 +1160,7 @@
mRecentsAnimationWrapper.finish(true /* toRecents */,
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
+ TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
doLogGesture(HOME);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java
new file mode 100644
index 0000000..8fc89f2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java
@@ -0,0 +1,72 @@
+/*
+ * 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.quickstep.hints;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class ChipsContainer extends FrameLayout {
+
+ private static final String TAG = "ChipsContainer";
+
+ public static final FloatProperty<ChipsContainer> HINT_VISIBILITY =
+ new FloatProperty<ChipsContainer>("hint_visibility") {
+ @Override
+ public void setValue(ChipsContainer chipsContainer, float v) {
+ chipsContainer.setHintVisibility(v);
+ }
+
+ @Override
+ public Float get(ChipsContainer chipsContainer) {
+ return chipsContainer.mHintVisibility;
+ }
+ };
+
+ private float mHintVisibility;
+
+ public ChipsContainer(Context context) {
+ super(context);
+ }
+
+ public ChipsContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public void setView(View v) {
+ removeAllViews();
+ addView(v);
+ }
+
+ public void setHintVisibility(float v) {
+ if (v == 1) {
+ setVisibility(VISIBLE);
+ } else {
+ setVisibility(GONE);
+ }
+ mHintVisibility = v;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java
deleted file mode 100644
index 22b1217..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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.quickstep.hints;
-
-import static com.android.quickstep.hints.UiHintListenerConstants.HINTS_KEY;
-import static com.android.quickstep.hints.UiHintListenerConstants.ON_HINTS_RETURNED_CODE;
-import static com.android.quickstep.hints.UiInterfaceConstants.REQUEST_HINTS_CODE;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-
-import java.util.ArrayList;
-
-public class HintsContainer extends LinearLayout {
-
- private static final String TAG = "HintsView";
-
- public static final FloatProperty<HintsContainer> HINT_VISIBILITY =
- new FloatProperty<HintsContainer>("hint_visibility") {
- @Override
- public void setValue(HintsContainer hintsContainer, float v) {
- hintsContainer.setHintVisibility(v);
- }
-
- @Override
- public Float get(HintsContainer hintsContainer) {
- return hintsContainer.mHintVisibility;
- }
- };
-
- private static Intent mServiceIntent =
- new Intent("com.android.systemui.action.UI_PULL_INTERFACE")
- .setClassName(
- "com.android.systemui.navbarhint",
- "com.android.systemui.navbarhint.service.HintService");
-
- @Nullable
- private Messenger mHintServiceInterface;
- private UiHintListener mUiHintListener;
- private boolean mBound = false;
- private float mHintVisibility;
-
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- mHintServiceInterface = new Messenger(iBinder);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- mHintServiceInterface = null;
- attemptBinding();
- }
-
- @Override
- public void onBindingDied(ComponentName componentName) {
- mHintServiceInterface = null;
- attemptBinding();
- }
- };
-
- public HintsContainer(Context context) {
- super(context);
- }
-
- public HintsContainer(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mUiHintListener == null) {
- mUiHintListener = new UiHintListener(this);
- }
- if (!mBound) {
- attemptBinding();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (mBound) {
- getContext().unbindService(mServiceConnection);
- mBound = false;
- }
- super.onDetachedFromWindow();
- }
-
- public void setHintVisibility(float v) {
- if (v == 1) {
- getHints();
- setVisibility(VISIBLE);
- } else {
- setVisibility(GONE);
- }
- mHintVisibility = v;
- }
-
- private void attemptBinding() {
- if (mBound) {
- getContext().unbindService(mServiceConnection);
- mBound = false;
- }
- boolean success = getContext().bindService(mServiceIntent,
- mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
- if (success) {
- mBound = true;
- } else {
- Log.w(TAG, "Binding to hint supplier failed");
- }
- }
-
- private void sendOnHintTap(Bundle hint) {
- if (mHintServiceInterface != null) {
- Message msg = Message.obtain(null, UiInterfaceConstants.ON_HINT_TAP_CODE);
- Bundle data = new Bundle();
- data.putString(UiInterfaceConstants.HINT_ID_KEY, HintUtil.getId(hint));
- data.putInt(UiInterfaceConstants.WIDTH_PX_KEY, getWidth());
- data.putInt(UiInterfaceConstants.HEIGHT_PX_KEY, getHeight());
- data.putInt(UiInterfaceConstants.HINT_SPACE_WIDTH_PX_KEY, 0);
- data.putInt(UiInterfaceConstants.HINT_SPACE_HEIGHT_PX_KEY, 0);
- msg.setData(data);
- try {
- mHintServiceInterface.send(msg);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send hint tap", e);
- }
- }
- }
-
- private void getHints() {
- if (mHintServiceInterface != null) {
- try {
- Message m = Message.obtain(null, REQUEST_HINTS_CODE);
- m.replyTo = new Messenger(mUiHintListener);
- mHintServiceInterface.send(m);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send message", e);
- }
- }
- }
-
- private static class UiHintListener extends Handler {
- private HintsContainer mView;
-
- UiHintListener(HintsContainer v) {
- mView = v;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ON_HINTS_RETURNED_CODE:
- handleHints(msg);
- break;
- default:
- Log.e(TAG, "UiPullInterface got unrecognized code: " + msg.what);
- break;
- }
- }
-
- private void handleHints(Message msg) {
- Bundle bundle = msg.getData();
- ArrayList<Bundle> hints = bundle.getParcelableArrayList(HINTS_KEY);
-
- if (hints != null) {
- mView.removeAllViews();
-
- for (Bundle hint : hints) {
- HintView h = (HintView) LayoutInflater.from(mView.getContext()).inflate(
- R.layout.hint, mView, false);
- h.setHint(hint);
- h.setOnClickListener((v) -> mView.sendOnHintTap(hint));
- mView.addView(h);
- }
- }
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 5fe92d5..0aa1beb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -50,6 +50,7 @@
public final class DigitalWellBeingToast extends LinearLayout {
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
+ static final int MINUTE_MS = 60000;
public interface InitializeCallback {
void call(float saturation, String contentDescription);
@@ -185,7 +186,11 @@
/* forceFormatWidth= */ false);
}
- private String getShorterReadableDuration(Duration duration) {
+ private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
+ final Duration duration = Duration.ofMillis(
+ remainingTime > MINUTE_MS ?
+ (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
+ remainingTime);
return getReadableDuration(
duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
}
@@ -196,7 +201,7 @@
resources.getString(R.string.app_in_grayscale) :
resources.getString(
R.string.time_left_for_app,
- getShorterReadableDuration(Duration.ofMillis(remainingTime)));
+ getRoundedUpToMinuteReadableDuration(remainingTime));
}
public void openAppUsageSettings() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 88fe2ee..97bce5e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -42,9 +42,10 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.OverviewInteractionState;
-import com.android.quickstep.hints.HintsContainer;
+import com.android.quickstep.hints.ChipsContainer;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.LayoutUtils;
@@ -77,7 +78,7 @@
private float mTranslationYFactor;
private final TransformParams mTransformParams = new TransformParams();
- private HintsContainer mHintsContainer;
+ private ChipsContainer mChipsContainer;
public LauncherRecentsView(Context context) {
this(context, null);
@@ -111,8 +112,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mHintsContainer = mActivity.findViewById(R.id.hints);
- mHintsContainer.setPadding(0, 0, 0, mActivity.getDeviceProfile().chipHintBottomMarginPx);
+ mChipsContainer = mActivity.findViewById(R.id.hints);
+ BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) mChipsContainer.getLayoutParams();
+ params.bottomMargin = mActivity.getDeviceProfile().chipHintBottomMarginPx;
}
public void setTranslationYFactor(float translationFactor) {
@@ -131,11 +133,15 @@
}
public void setHintVisibility(float v) {
- if (mHintsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
- mHintsContainer.setHintVisibility(v);
+ if (mChipsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
+ mChipsContainer.setHintVisibility(v);
}
}
+ public ChipsContainer getChipsContainer() {
+ return mChipsContainer;
+ }
+
@Override
public void draw(Canvas canvas) {
maybeDrawEmptyMessage(canvas);
@@ -192,7 +198,7 @@
if (ENABLE_HINTS_IN_OVERVIEW.get()) {
anim.anim.play(ObjectAnimator.ofFloat(
- mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+ mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
}
return anim;
@@ -206,10 +212,10 @@
if (ENABLE_HINTS_IN_OVERVIEW.get()) {
anim.anim.play(ObjectAnimator.ofFloat(
- mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+ mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
anim.addEndListener(onEndListener -> {
if (!onEndListener.isSuccess) {
- mHintsContainer.setHintVisibility(1);
+ mChipsContainer.setHintVisibility(1);
}
});
}
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 a7bf2c3..3e0e8ae 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
@@ -773,7 +773,7 @@
setCurrentTask(runningTaskId);
}
- public TaskView getRunningTaskView() {
+ public @Nullable TaskView getRunningTaskView() {
return getTaskView(mRunningTaskId);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index fb58c24..419a666 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -19,6 +19,7 @@
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.BaseActivity.fromContext;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -91,7 +92,6 @@
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
- private static final long TASK_LAUNCH_ANIM_DURATION = 200;
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@@ -237,10 +237,10 @@
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation =
- getRecentsView().createTaskLauncherAnimation(this, TASK_LAUNCH_ANIM_DURATION);
- pendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+ getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
+ pendingAnimation.anim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
- .wrap(pendingAnimation.anim, TASK_LAUNCH_ANIM_DURATION, null);
+ .wrap(pendingAnimation.anim, RECENTS_LAUNCH_DURATION, null);
currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false);
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 2626481..f5e5dd3 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -39,7 +39,6 @@
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
<dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
<dimen name="motion_pause_detector_min_displacement">48dp</dimen>
- <dimen name="motion_pause_detector_max_orthogonal_displacement">48dp</dimen>
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
@@ -65,4 +64,8 @@
<dimen name="shelf_surface_radius">16dp</dimen>
<!-- same as vertical_drag_handle_size -->
<dimen name="shelf_surface_offset">24dp</dimen>
+
+ <!-- Assistant Gestures -->
+ <dimen name="gestures_assistant_width">70dp</dimen>
+ <dimen name="gestures_assistant_threshold">200dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index ab5f479..f8b167b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -403,9 +403,7 @@
private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
- if (mFloatingView == null) {
- mFloatingView = new FloatingIconView(mLauncher);
- } else {
+ if (mFloatingView != null) {
mFloatingView.setTranslationX(0);
mFloatingView.setTranslationY(0);
mFloatingView.setScaleX(1);
@@ -414,7 +412,8 @@
mFloatingView.setBackground(null);
}
Rect rect = new Rect();
- mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
+ mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
+ true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView);
int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
@@ -615,11 +614,6 @@
WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-
- definition.addRemoteAnimation(
- WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(true /* fromUnlock */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 411e593..a0ab301 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -175,7 +175,7 @@
}
}
- public void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
+ private void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
sendToTarget();
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 81a22a1..56bc8570 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,12 +16,15 @@
package com.android.quickstep;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
+import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;
@@ -59,8 +62,8 @@
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
- private float mWindowCornerRadius = -1;
- private Boolean mSupportsRoundedCornersOnWindows;
+ private float mWindowCornerRadius = 0;
+ private boolean mSupportsRoundedCornersOnWindows;
private RecentsModel(Context context) {
mContext = context;
@@ -73,6 +76,12 @@
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
+ public void onInitializeSystemUI(Bundle params) {
+ mWindowCornerRadius = params.getFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, 0);
+ mSupportsRoundedCornersOnWindows =
+ params.getBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, false);
+ }
+
public TaskIconCache getIconCache() {
return mIconCache;
}
@@ -182,42 +191,10 @@
}
public float getWindowCornerRadius() {
- // The window corner radius is expressed in pixels and won't change if the
- // display density changes. It's safe to cache the value.
- if (mWindowCornerRadius == -1) {
- if (mSystemUiProxy != null) {
- try {
- mWindowCornerRadius = mSystemUiProxy.getWindowCornerRadius();
- } catch (RemoteException e) {
- Log.w(TAG, "Connection to ISystemUIProxy was lost, ignoring window corner "
- + "radius");
- return 0;
- }
- } else {
- Log.w(TAG, "ISystemUIProxy is null, ignoring window corner radius");
- return 0;
- }
- }
return mWindowCornerRadius;
}
public boolean supportsRoundedCornersOnWindows() {
- if (mSupportsRoundedCornersOnWindows == null) {
- if (mSystemUiProxy != null) {
- try {
- mSupportsRoundedCornersOnWindows =
- mSystemUiProxy.supportsRoundedCornersOnWindows();
- } catch (RemoteException e) {
- Log.w(TAG, "Connection to ISystemUIProxy was lost, ignoring window corner "
- + "radius");
- return false;
- }
- } else {
- Log.w(TAG, "ISystemUIProxy is null, ignoring window corner radius");
- return false;
- }
- }
-
return mSupportsRoundedCornersOnWindows;
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 612c00e..07af9b3 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -24,14 +26,17 @@
import android.os.Looper;
import android.util.LruCache;
import android.view.accessibility.AccessibilityManager;
+
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.uioverrides.RecentsUiFactory;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+
import java.util.function.Consumer;
/**
@@ -125,8 +130,9 @@
return label;
}
- // Skip loading content descriptions if accessibility is not enabled
- if (!mAccessibilityManager.isEnabled()) {
+ // Skip loading content descriptions if accessibility is disabled unless low RAM recents
+ // is enabled.
+ if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
return "";
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 21d8144..8a117c8 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -40,7 +40,6 @@
private final float mSpeedSomewhatFast;
private final float mSpeedFast;
private final float mMinDisplacementForPause;
- private final float mMaxOrthogonalDisplacementForPause;
private final Alarm mForcePauseTimeout;
private Long mPreviousTime = null;
@@ -62,8 +61,6 @@
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
- mMaxOrthogonalDisplacementForPause = res.getDimension(
- R.dimen.motion_pause_detector_max_orthogonal_displacement);
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
}
@@ -77,7 +74,6 @@
if (mOnMotionPauseListener != null) {
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
}
- mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
}
/**
@@ -94,6 +90,7 @@
if (mFirstOrthogonalPosition == null) {
mFirstOrthogonalPosition = orthogonalPosition;
}
+ mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
long time = SystemClock.uptimeMillis();
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
@@ -108,7 +105,6 @@
}
mPreviousTime = time;
mPreviousPosition = position;
- mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
}
private void checkMotionPaused(float velocity, float prevVelocity,
@@ -135,9 +131,11 @@
}
}
boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
- boolean passedMaxOrthogonalDisplacement =
- totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
- isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
+ boolean isDisplacementOrthogonal = totalDisplacement.orthogonal > totalDisplacement.primary;
+ if (!passedMinDisplacement || isDisplacementOrthogonal) {
+ mForcePauseTimeout.cancelAlarm();
+ isPaused = false;
+ }
updatePaused(isPaused);
}
diff --git a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
index 7274090..f89842a 100644
--- a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
+++ b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
@@ -19,6 +19,12 @@
import static com.android.quickstep.QuickStepOnOffRule.Mode.BOTH;
import static com.android.quickstep.QuickStepOnOffRule.Mode.OFF;
import static com.android.quickstep.QuickStepOnOffRule.Mode.ON;
+import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
+
+import static org.junit.Assert.assertTrue;
+
+import android.provider.Settings;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -41,6 +47,8 @@
*/
public class QuickStepOnOffRule implements TestRule {
+ static final String TAG = "QuickStepOnOffRule";
+
public enum Mode {
ON, OFF, BOTH
}
@@ -68,39 +76,53 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- try {
- if (mode == ON || mode == BOTH) {
- evaluateWithQuickstepOn();
+ if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
+ try {
+ if (mode == ON || mode == BOTH) {
+ evaluateWithQuickstepOn();
+ }
+ if (mode == OFF || mode == BOTH) {
+ evaluateWithQuickstepOff();
+ }
+ } finally {
+ setSwipeUpSetting(null);
}
- if (mode == OFF || mode == BOTH) {
- evaluateWithQuickstepOff();
+ } else {
+ // Execute without changing the setting, if the requested mode is
+ // compatible.
+ final boolean swipeUpEnabledDefaultValue =
+ SwipeUpSetting.isSwipeUpEnabledDefaultValue();
+ if (mode == BOTH ||
+ mode == ON && swipeUpEnabledDefaultValue ||
+ mode == OFF && !swipeUpEnabledDefaultValue) {
+ evaluateWithoutChangingSetting(base);
}
- } finally {
- overrideSwipeUpEnabled(null);
}
}
- private void evaluateWithQuickstepOff() throws Throwable {
- overrideSwipeUpEnabled(false);
+ public void setSwipeUpSetting(String value) {
+ Log.d(TAG, "setSwipeUpSetting: " + value);
+ assertTrue("Couldn't change Quickstep mode",
+ Settings.Secure.putString(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().
+ getContentResolver(),
+ SWIPE_UP_SETTING_NAME,
+ value));
+ }
+
+ public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
base.evaluate();
}
+ private void evaluateWithQuickstepOff() throws Throwable {
+ setSwipeUpSetting("0");
+ evaluateWithoutChangingSetting(base);
+ }
+
private void evaluateWithQuickstepOn() throws Throwable {
- overrideSwipeUpEnabled(true);
+ setSwipeUpSetting("1");
base.evaluate();
}
-
- private void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride)
- throws Throwable {
- mLauncher.overrideSwipeUpEnabled(swipeUpEnabledOverride);
- mMainThreadExecutor.execute(() -> OverviewInteractionState.INSTANCE.get(
- InstrumentationRegistry.getInstrumentation().getTargetContext()).
- notifySwipeUpSettingChanged(mLauncher.isSwipeUpEnabled()));
- // TODO(b/124236673): avoid using sleep().
- mLauncher.getDevice().waitForIdle();
- Thread.sleep(2000);
- mLauncher.getDevice().waitForIdle();
- }
};
} else {
return base;
diff --git a/res/drawable/horizontal_ellipsis.xml b/res/drawable/horizontal_ellipsis.xml
index ad08529..65e958d 100644
--- a/res/drawable/horizontal_ellipsis.xml
+++ b/res/drawable/horizontal_ellipsis.xml
@@ -22,6 +22,6 @@
android:tint="?android:attr/textColorSecondary" >
<path
- android:pathData="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
+ android:pathData="M6 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm12 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm-6 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"
android:fillColor="@android:color/white" />
</vector>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 91c3705..c9cea80 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -38,6 +38,15 @@
android:theme="@style/HomeScreenElementTheme"
launcher:pageIndicator="@+id/page_indicator" />
+ <!-- DO NOT CHANGE THE ID -->
+ <com.android.launcher3.Hotseat
+ android:id="@+id/hotseat"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@style/HomeScreenElementTheme"
+ android:importantForAccessibility="no"
+ launcher:containerType="hotseat" />
+
<include
android:id="@+id/overview_panel"
layout="@layout/overview_panel"
@@ -71,15 +80,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.Hotseat
- android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:theme="@style/HomeScreenElementTheme"
- android:importantForAccessibility="no"
- launcher:containerType="hotseat" />
-
</com.android.launcher3.dragndrop.DragLayer>
</com.android.launcher3.LauncherRootView>
diff --git a/res/layout/snackbar.xml b/res/layout/snackbar.xml
index bca3308..b818943 100644
--- a/res/layout/snackbar.xml
+++ b/res/layout/snackbar.xml
@@ -29,7 +29,7 @@
android:ellipsize="end"
android:textSize="@dimen/snackbar_max_text_size"
android:textColor="?android:attr/textColorPrimary"
- android:theme="@style/TextTitle"/>
+ style="@style/TextTitle"/>
<TextView
android:id="@+id/action"
android:layout_height="@dimen/snackbar_content_height"
@@ -42,6 +42,6 @@
android:textStyle="bold"
android:textSize="@dimen/snackbar_max_text_size"
android:textColor="?android:attr/colorAccent"
- android:theme="@style/TextTitle"
+ style="@style/TextTitle"
android:capitalize="sentences"/>
</merge>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index e5b1448..62bc53a 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -24,7 +24,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -33,9 +32,11 @@
import android.os.Process;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.Patterns;
+import android.util.Xml;
import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -149,9 +150,6 @@
private static final String HOTSEAT_CONTAINER_NAME =
Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
- private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
- "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
-
@Thunk final Context mContext;
@Thunk final AppWidgetHost mAppWidgetHost;
protected final LayoutParserCallback mCallback;
@@ -207,7 +205,7 @@
*/
protected int parseLayout(int layoutId, IntArray screenIds)
throws XmlPullParserException, IOException {
- XmlResourceParser parser = mSourceRes.getXml(layoutId);
+ XmlPullParser parser = mSourceRes.getXml(layoutId);
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
int type;
@@ -228,7 +226,7 @@
* Parses container and screenId attribute from the current tag, and puts it in the out.
* @param out array of size 2.
*/
- protected void parseContainerAndScreen(XmlResourceParser parser, int[] out) {
+ protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
out[0] = Favorites.CONTAINER_HOTSEAT;
// Hack: hotseat items are stored using screen ids
@@ -243,7 +241,7 @@
* Parses the current node and returns the number of elements added.
*/
protected int parseAndAddNode(
- XmlResourceParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
+ XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
@@ -324,7 +322,7 @@
* Parses the tag and adds to the db
* @return the id of the row added or -1;
*/
- int parseAndAdd(XmlResourceParser parser)
+ int parseAndAdd(XmlPullParser parser)
throws XmlPullParserException, IOException;
}
@@ -334,7 +332,7 @@
protected class AppShortcutParser implements TagParser {
@Override
- public int parseAndAdd(XmlResourceParser parser) {
+ public int parseAndAdd(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@@ -371,7 +369,7 @@
/**
* Helper method to allow extending the parser capabilities
*/
- protected int invalidPackageOrClass(XmlResourceParser parser) {
+ protected int invalidPackageOrClass(XmlPullParser parser) {
Log.w(TAG, "Skipping invalid <favorite> with no component");
return -1;
}
@@ -383,7 +381,7 @@
protected class AutoInstallParser implements TagParser {
@Override
- public int parseAndAdd(XmlResourceParser parser) {
+ public int parseAndAdd(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
@@ -414,7 +412,7 @@
}
@Override
- public int parseAndAdd(XmlResourceParser parser) {
+ public int parseAndAdd(XmlPullParser parser) {
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
@@ -449,7 +447,7 @@
intent, Favorites.ITEM_TYPE_SHORTCUT);
}
- protected Intent parseIntent(XmlResourceParser parser) {
+ protected Intent parseIntent(XmlPullParser parser) {
final String url = getAttributeValue(parser, ATTR_URL);
if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
@@ -470,7 +468,7 @@
protected class PendingWidgetParser implements TagParser {
@Override
- public int parseAndAdd(XmlResourceParser parser)
+ public int parseAndAdd(XmlPullParser parser)
throws XmlPullParserException, IOException {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@@ -541,7 +539,7 @@
}
@Override
- public int parseAndAdd(XmlResourceParser parser)
+ public int parseAndAdd(XmlPullParser parser)
throws XmlPullParserException, IOException {
final String title;
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
@@ -649,7 +647,7 @@
* Return attribute value, attempting launcher-specific namespace first
* before falling back to anonymous attribute.
*/
- protected static String getAttributeValue(XmlResourceParser parser, String attribute) {
+ protected static String getAttributeValue(XmlPullParser parser, String attribute) {
String value = parser.getAttributeValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
if (value == null) {
@@ -662,13 +660,14 @@
* Return attribute resource value, attempting launcher-specific namespace
* first before falling back to anonymous attribute.
*/
- protected static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+ protected static int getAttributeResourceValue(XmlPullParser parser, String attribute,
int defaultValue) {
- int value = parser.getAttributeResourceValue(
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ int value = attrs.getAttributeResourceValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
defaultValue);
if (value == defaultValue) {
- value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+ value = attrs.getAttributeResourceValue(null, attribute, defaultValue);
}
return value;
}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 44830e8..75297f6 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -10,7 +10,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -76,7 +75,7 @@
}
@Override
- protected void parseContainerAndScreen(XmlResourceParser parser, int[] out) {
+ protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
if (strContainer != null) {
@@ -91,7 +90,7 @@
public class AppShortcutWithUriParser extends AppShortcutParser {
@Override
- protected int invalidPackageOrClass(XmlResourceParser parser) {
+ protected int invalidPackageOrClass(XmlPullParser parser) {
final String uri = getAttributeValue(parser, ATTR_URI);
if (TextUtils.isEmpty(uri)) {
Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
@@ -185,7 +184,7 @@
}
@Override
- protected Intent parseIntent(XmlResourceParser parser) {
+ protected Intent parseIntent(XmlPullParser parser) {
String uri = null;
try {
uri = getAttributeValue(parser, ATTR_URI);
@@ -205,7 +204,7 @@
private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
@Override
- public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+ public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
IOException {
final int groupDepth = parser.getDepth();
int type;
@@ -233,7 +232,7 @@
@Thunk class PartnerFolderParser implements TagParser {
@Override
- public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+ public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
IOException {
// Folder contents come from an external XML resource
final Partner partner = Partner.get(mPackageManager);
@@ -242,7 +241,7 @@
final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
"xml", partner.getPackageName());
if (resId != 0) {
- final XmlResourceParser partnerParser = partnerRes.getXml(resId);
+ final XmlPullParser partnerParser = partnerRes.getXml(resId);
beginDocument(partnerParser, TAG_FOLDER);
FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
@@ -259,7 +258,7 @@
@Thunk class MyFolderParser extends FolderParser {
@Override
- public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+ public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
IOException {
final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
if (resId != 0) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 01535b0..3b054c2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -611,6 +611,12 @@
outBounds.right = outBounds.left + (getCellSize().x * spanX);
}
+ public float getAspectRatioWithInsets() {
+ int w = widthPx - mInsets.left - mInsets.right;
+ int h = heightPx - mInsets.top - mInsets.bottom;
+ return ((float) Math.max(w, h)) / Math.min(w, h);
+ }
+
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f571aa3..d820448 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -257,7 +257,7 @@
private RotationHelper mRotationHelper;
- private final Handler mHandler = new Handler();
+ final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@Override
@@ -319,6 +319,7 @@
}
}
restoreState(savedInstanceState);
+ mStateManager.reapplyState();
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
@@ -1747,6 +1748,8 @@
setWorkspaceLoading(true);
// Clear the workspace because it's going to be rebound
+ mDragController.cancelDrag();
+
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
mAppWidgetHost.clearViews();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5ab6eea..b1664f1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -34,6 +34,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
@@ -210,20 +211,20 @@
final int itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
- // check all the data is consistent
- if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
- ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
- ShortcutInfo shortcut = (ShortcutInfo) item;
- if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
- modelShortcut.intent.filterEquals(shortcut.intent) &&
- modelShortcut.id == shortcut.id &&
- modelShortcut.itemType == shortcut.itemType &&
- modelShortcut.container == shortcut.container &&
- modelShortcut.screenId == shortcut.screenId &&
- modelShortcut.cellX == shortcut.cellX &&
- modelShortcut.cellY == shortcut.cellY &&
- modelShortcut.spanX == shortcut.spanX &&
- modelShortcut.spanY == shortcut.spanY) {
+ // If it is a release build on a release device, check all the data is consistent as
+ // we don't want to crash non-dev users.
+ if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
+ modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+ if (modelItem.title.toString().equals(item.title.toString()) &&
+ modelItem.getIntent().filterEquals(item.getIntent()) &&
+ modelItem.id == item.id &&
+ modelItem.itemType == item.itemType &&
+ modelItem.container == item.container &&
+ modelItem.screenId == item.screenId &&
+ modelItem.cellX == item.cellX &&
+ modelItem.cellY == item.cellY &&
+ modelItem.spanX == item.spanX &&
+ modelItem.spanY == item.spanY) {
// For all intents and purposes, this is the same object
return;
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index fb33694..ce73229 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import android.annotation.TargetApi;
+import android.app.backup.BackupManager;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
@@ -150,7 +151,7 @@
mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
if (RestoreDbTask.isPending(getContext())) {
- if (!RestoreDbTask.performRestore(mOpenHelper)) {
+ if (!RestoreDbTask.performRestore(mOpenHelper, new BackupManager(getContext()))) {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
}
// Set is pending to false irrespective of the result, so that it doesn't get
@@ -542,6 +543,7 @@
* The class is subclassed in tests to create an in-memory db.
*/
public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
+ private final BackupManager mBackupManager;
private final Handler mWidgetHostResetHandler;
private final Context mContext;
private int mMaxItemId = -1;
@@ -571,6 +573,7 @@
super(context, tableName, SCHEMA_VERSION);
mContext = context;
mWidgetHostResetHandler = widgetHostResetHandler;
+ mBackupManager = new BackupManager(mContext);
}
protected void initIds() {
@@ -620,9 +623,12 @@
Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
}
+ public long getSerialNumberForUser(UserHandle user) {
+ return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(user);
+ }
+
public long getDefaultUserSerial() {
- return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
- Process.myUserHandle());
+ return getSerialNumberForUser(Process.myUserHandle());
}
private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6d85612..cee1c26 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -187,6 +187,11 @@
return new float[] {1, 0, 0};
}
+ public float[] getHotseatScaleAndTranslation(Launcher launcher) {
+ // For most states, treat the hotseat as if it were part of the workspace.
+ return getWorkspaceScaleAndTranslation(launcher);
+ }
+
/**
* Returns 2 floats designating how to transition overview:
* scale for the current and adjacent pages
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index a877141..f6b54f2 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import static android.view.View.VISIBLE;
-
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
@@ -249,6 +248,22 @@
return;
}
+ if (delay > 0) {
+ // Create the animation after the delay as some properties can change between preparing
+ // the animation and running the animation.
+ int startChangeId = mConfig.mChangeId;
+ mUiHandler.postDelayed(() -> {
+ if (mConfig.mChangeId == startChangeId) {
+ goToStateAnimated(state, fromState, onCompleteRunnable);
+ }
+ }, delay);
+ } else {
+ goToStateAnimated(state, fromState, onCompleteRunnable);
+ }
+ }
+
+ private void goToStateAnimated(LauncherState state, LauncherState fromState,
+ Runnable onCompleteRunnable) {
// Since state NORMAL can be reached from multiple states, just assume that the
// transition plays in reverse and use the same duration as previous state.
mConfig.duration = state == NORMAL ? fromState.transitionDuration : state.transitionDuration;
@@ -257,12 +272,7 @@
prepareForAtomicAnimation(fromState, state, builder);
AnimatorSet animation = createAnimationToNewWorkspaceInternal(
state, builder, onCompleteRunnable);
- Runnable runnable = new StartAnimRunnable(animation);
- if (delay > 0) {
- mUiHandler.postDelayed(runnable, delay);
- } else {
- mUiHandler.post(runnable);
- }
+ mUiHandler.post(new StartAnimRunnable(animation));
}
/**
@@ -298,6 +308,8 @@
if (!isWorkspaceVisible) {
workspace.setScaleX(0.92f);
workspace.setScaleY(0.92f);
+ workspace.getHotseat().setScaleX(0.92f);
+ workspace.getHotseat().setScaleY(0.92f);
}
}
}
@@ -532,6 +544,8 @@
private AnimatorSet mCurrentAnimation;
private LauncherState mTargetState;
+ // Id to keep track of config changes, to tie an animation with the corresponding request
+ private int mChangeId = 0;
/**
* Cancels the current animation and resets config variables.
@@ -553,6 +567,7 @@
mCurrentAnimation = null;
playbackController = null;
+ mChangeId ++;
}
public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 00da6fa..7fa1aa0 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1027,7 +1027,8 @@
if (overScrollAmount < 0) {
super.scrollTo(overScrollAmount, getScrollY());
} else {
- super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
+ int x = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
+ super.scrollTo(x + overScrollAmount, getScrollY());
}
invalidate();
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 832e9c9..e699500 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -37,6 +38,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -55,14 +57,24 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -556,6 +568,7 @@
public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
final DragLayer dragLayer = launcher.getDragLayer();
final boolean isBubbleTextView = v instanceof BubbleTextView;
+ final boolean isFolderIcon = v instanceof FolderIcon;
final Rect rect = new Rect();
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
@@ -563,15 +576,14 @@
// Deep shortcut views have their icon drawn in a separate view.
DeepShortcutView view = (DeepShortcutView) v.getParent();
dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
- } else if (isBubbleTextView && v.getTag() instanceof ItemInfo
+ } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
&& (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
|| ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
- BubbleTextView btv = (BubbleTextView) v;
- CellLayout pageViewIsOn = ((CellLayout) btv.getParent().getParent());
+ CellLayout pageViewIsOn = ((CellLayout) v.getParent().getParent());
int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
DeviceProfile dp = launcher.getDeviceProfile();
- ItemInfo info = ((ItemInfo) btv.getTag());
+ ItemInfo info = ((ItemInfo) v.getTag());
dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
} else {
@@ -581,8 +593,9 @@
int viewLocationTop = rect.top;
if (isBubbleTextView && !fromDeepShortcutView) {
- BubbleTextView btv = (BubbleTextView) v;
- btv.getIconBounds(rect);
+ ((BubbleTextView) v).getIconBounds(rect);
+ } else if (isFolderIcon) {
+ ((FolderIcon) v).getPreviewBounds(rect);
} else {
rect.set(0, 0, rect.width(), rect.height());
}
@@ -595,6 +608,51 @@
public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
try {
context.unregisterReceiver(receiver);
- } catch (IllegalArgumentException e) { }
+ } catch (IllegalArgumentException e) {}
+ }
+
+ /**
+ * Returns the full drawable for {@param info}.
+ * @param outObj this is set to the internal data associated with {@param info},
+ * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+ */
+ public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
+ boolean flattenDrawable, Object[] outObj) {
+ LauncherAppState appState = LauncherAppState.getInstance(launcher);
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
+ .resolveActivity(info.getIntent(), info.user);
+ outObj[0] = activityInfo;
+ return (activityInfo != null) ? appState.getIconCache()
+ .getFullResIcon(activityInfo, flattenDrawable) : null;
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (info instanceof PendingAddShortcutInfo) {
+ ShortcutConfigActivityInfo activityInfo =
+ ((PendingAddShortcutInfo) info).activityInfo;
+ outObj[0] = activityInfo;
+ return activityInfo.getFullResIcon(appState.getIconCache());
+ }
+ ShortcutKey key = ShortcutKey.fromItemInfo(info);
+ DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
+ List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+ key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ outObj[0] = si.get(0);
+ return sm.getShortcutIconDrawable(si.get(0),
+ appState.getInvariantDeviceProfile().fillResIconDpi);
+ }
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
+ launcher, info.id, new Point(width, height));
+ if (icon == null) {
+ return null;
+ }
+ outObj[0] = icon;
+ return icon;
+ } else {
+ return null;
+ }
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7f5ca42..09eb6d9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -19,9 +19,13 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import android.animation.Animator;
@@ -34,7 +38,6 @@
import android.app.WallpaperManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -44,8 +47,10 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -98,8 +103,6 @@
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
import java.util.function.Predicate;
/**
@@ -521,6 +524,9 @@
mScreenOrder.clear();
mWorkspaceScreens.clear();
+ // Remove any deferred refresh callbacks
+ mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class);
+
// Ensure that the first page is always present
bindAndInitFirstWorkspaceScreen(qsb);
@@ -1608,7 +1614,7 @@
boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
boolean willBecomeShortcut =
- (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+ (info.itemType == ITEM_TYPE_APPLICATION ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
@@ -2516,7 +2522,7 @@
View view;
switch (info.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ case ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
if (info.container == NO_ID && info instanceof AppInfo) {
@@ -2892,51 +2898,29 @@
}
/**
- * Returns a list of all the CellLayouts on the Homescreen, starting with
- * {@param startPage}, then going outward alternating between pages prior to the startPage,
- * and then the pages after the startPage.
- * ie. if there are 5 pages [0, 1, 2, 3, 4] and startPage is 1, we return [1, 0, 2, 3, 4].
- */
- private CellLayout[] getWorkspaceCellLayouts(int startPage) {
- int screenCount = getChildCount();
- final CellLayout[] layouts = new CellLayout[screenCount];
- int screen = 0;
-
- layouts[screen] = (CellLayout) getChildAt(startPage);
- screen++;
-
- for (int i = 1; screen < screenCount; ++i) {
- CellLayout prevPage = (CellLayout) getChildAt(startPage - i);
- CellLayout nextPage = (CellLayout) getChildAt(startPage + i);
-
- if (prevPage != null) {
- layouts[screen] = prevPage;
- screen++;
- }
- if (nextPage != null) {
- layouts[screen] = nextPage;
- screen++;
- }
- }
- return layouts;
- }
-
- /**
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
- * @param component The component of the task being dismissed.
+ * @param packageName The package name of the app to match.
+ * @param user The user of the app to match.
*/
- public View getFirstMatchForAppClose(ComponentName component) {
+ public View getFirstMatchForAppClose(String packageName, UserHandle user) {
final int curPage = getCurrentPage();
final CellLayout currentPage = (CellLayout) getPageAt(curPage);
- final Workspace.ItemOperator isItemComponent = (info, view) ->
- info != null && Objects.equals(info.getTargetComponent(), component);
- final Workspace.ItemOperator isItemInFolder = (info, view) -> {
+ final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
+ && info.getTargetComponent() != null
+ && TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
+ && info.user.equals(user);
+ final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
+ packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
+ final Workspace.ItemOperator packageAndUserAndShortcut = (ItemInfo info, View view) ->
+ packageAndUser.evaluate(info, view) && (info.itemType == ITEM_TYPE_SHORTCUT
+ || info.itemType == ITEM_TYPE_DEEP_SHORTCUT);
+ final Workspace.ItemOperator packageAndUserInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (ShortcutInfo shortcutInfo : folderInfo.contents) {
- if (Objects.equals(shortcutInfo.getTargetComponent(), component)) {
+ if (packageAndUser.evaluate(shortcutInfo, view)) {
return true;
}
}
@@ -2944,33 +2928,16 @@
return false;
};
- CellLayout[] hotseatAndCurrentPage = new CellLayout[] { getHotseat(), currentPage };
- // First we look if the app itself is in the hotseat or on the current workspace page.
- View icon = getFirstMatch(hotseatAndCurrentPage, isItemComponent);
- if (icon != null) {
- return icon;
+ // Order: App icons, shortcuts, app/shortcut in folder. Items in hotseat get returned first.
+ if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
+ return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
+ packageAndUserAndApp, packageAndUserAndShortcut, packageAndUserInFolder);
+ } else {
+ // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
+ // FolderAdaptiveIcon as the background.
+ return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
+ packageAndUserAndApp, packageAndUserAndShortcut);
}
- // Then we look if the app is in a folder on the hotseat or current workspace page.
- icon = getFirstMatch(hotseatAndCurrentPage, isItemInFolder);
- if (icon != null) {
- return icon;
- }
- // Continue searching for the app or for a folder with the app on other pages of the
- // workspace. We skip the current page, since we already searched above.
- CellLayout[] allPages = getWorkspaceCellLayouts(curPage);
- CellLayout[] page = new CellLayout[1];
- for (int i = 1; i < allPages.length; ++i) {
- page[0] = allPages[i];
- icon = getFirstMatch(page, isItemComponent);
- if (icon != null) {
- return icon;
- }
- icon = getFirstMatch(page, isItemInFolder);
- if (icon != null) {
- return icon;
- }
- }
- return null;
}
public View getHomescreenIconByItemId(final int id) {
@@ -2998,21 +2965,38 @@
return value[0];
}
- private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator operator) {
- final View[] value = new View[1];
+ /**
+ * @param cellLayouts List of CellLayouts to scan, in order of preference.
+ * @param operators List of operators, in order starting from best matching operator.
+ * @return
+ */
+ private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
+ // This array is filled with the first match for each operator.
+ final View[] matches = new View[operators.length];
+ // For efficiency, the outer loop should be CellLayout.
for (CellLayout cellLayout : cellLayouts) {
mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
- if (operator.evaluate(info, v)) {
- value[0] = v;
- return true;
+ for (int i = 0; i < operators.length; ++i) {
+ if (matches[i] == null && operators[i].evaluate(info, v)) {
+ matches[i] = v;
+ if (i == 0) {
+ // We can return since this is the best match possible.
+ return true;
+ }
+ }
}
return false;
});
- if (value[0] != null) {
+ if (matches[0] != null) {
break;
}
}
- return value[0];
+ for (View match : matches) {
+ if (match != null) {
+ return match;
+ }
+ }
+ return null;
}
void clearDropTargets() {
@@ -3348,13 +3332,15 @@
LauncherAppWidgetHost host) {
mInfos = infos;
mHost = host;
- mHandler = new Handler();
+ mHandler = mLauncher.mHandler;
mRefreshPending = true;
mHost.addProviderChangeListener(this);
// Force refresh after 10 seconds, if we don't get the provider changed event.
// This could happen when the provider is no longer available in the app.
- mHandler.postDelayed(this, 10000);
+ Message msg = Message.obtain(mHandler, this);
+ msg.obj = DeferredWidgetRefresh.class;
+ mHandler.sendMessageDelayed(msg, 10000);
}
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 3e09493..0507470 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -35,6 +34,7 @@
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
/**
@@ -72,6 +72,7 @@
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
AnimatorSetBuilder builder, AnimationConfig config) {
float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+ float[] hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(mLauncher);
mNewScale = scaleAndTranslation[0];
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
@@ -84,12 +85,24 @@
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
boolean playAtomicComponent = config.playAtomicComponent();
+ Hotseat hotseat = mWorkspace.getHotseat();
if (playAtomicComponent) {
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+
+ // Set the hotseat's pivot point to match the workspace's, so that it scales together.
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ int[] workspacePivot = new int[]{(int) mWorkspace.getPivotX(),
+ (int) mWorkspace.getPivotY()};
+ dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
+ dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
+ hotseat.setPivotX(workspacePivot[0]);
+ hotseat.setPivotY(workspacePivot[1]);
+ float hotseatScale = hotseatScaleAndTranslation[0];
+ propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
+
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
- propertySetter.setViewAlpha(mLauncher.getHotseat(), hotseatIconsAlpha,
- fadeInterpolator);
+ propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
hotseatIconsAlpha, fadeInterpolator);
}
@@ -105,6 +118,11 @@
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
scaleAndTranslation[2], translationInterpolator);
+ propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
+ hotseatScaleAndTranslation[2], translationInterpolator);
+ propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
+ hotseatScaleAndTranslation[2], translationInterpolator);
+
// Set scrim
WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index bcb5eec..93bf69d 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -124,12 +124,6 @@
float shiftCurrent = progress * mShiftRange;
mAppsView.setTranslationY(shiftCurrent);
- float hotseatTranslation = -mShiftRange + shiftCurrent;
-
- if (!mIsVerticalLayout) {
- mLauncher.getHotseat().setTranslationY(hotseatTranslation);
- mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
- }
// Use a light system UI (dark icons) if all apps is behind at least half of the
// status bar.
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 882529d..d6e450a 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -101,6 +101,10 @@
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");
+ public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
+ "ADAPTIVE_ICON_WINDOW_ANIM", true,
+ "Use adaptive icons for window animations.");
+
public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
@@ -112,6 +116,10 @@
"ENABLE_HINTS_IN_OVERVIEW", false,
"Show chip hints and gleams on the overview screen");
+ public static final TogglableFlag ENABLE_ASSISTANT_GESTURE = new ToggleableGlobalSettingsFlag(
+ "ENABLE_ASSISTANT_GESTURE", false,
+ "Enable swipe up from the bottom right corner to start assistant");
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8f223a3..bdbea29 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,7 +24,6 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
-import android.content.pm.LauncherActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -54,18 +53,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.Arrays;
-import java.util.List;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -204,11 +197,12 @@
public void run() {
LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
Object[] outObj = new Object[1];
- Drawable dr = getFullDrawable(info, appState, outObj);
+ int w = mBitmap.getWidth();
+ int h = mBitmap.getHeight();
+ Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h,
+ false /* flattenDrawable */, outObj);
if (dr instanceof AdaptiveIconDrawable) {
- int w = mBitmap.getWidth();
- int h = mBitmap.getHeight();
int blurMargin = (int) mLauncher.getResources()
.getDimension(R.dimen.blur_size_medium_outline) / 2;
@@ -314,49 +308,6 @@
}
/**
- * Returns the full drawable for {@param info}.
- * @param outObj this is set to the internal data associated with {@param info},
- * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
- */
- private Drawable getFullDrawable(ItemInfo info, LauncherAppState appState, Object[] outObj) {
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
- .resolveActivity(info.getIntent(), info.user);
- outObj[0] = activityInfo;
- return (activityInfo != null) ? appState.getIconCache()
- .getFullResIcon(activityInfo, false) : null;
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- if (info instanceof PendingAddShortcutInfo) {
- ShortcutConfigActivityInfo activityInfo =
- ((PendingAddShortcutInfo) info).activityInfo;
- outObj[0] = activityInfo;
- return activityInfo.getFullResIcon(appState.getIconCache());
- }
- ShortcutKey key = ShortcutKey.fromItemInfo(info);
- DeepShortcutManager sm = DeepShortcutManager.getInstance(mLauncher);
- List<ShortcutInfoCompat> si = sm.queryForFullDetails(
- key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
- if (si.isEmpty()) {
- return null;
- } else {
- outObj[0] = si.get(0);
- return sm.getShortcutIconDrawable(si.get(0),
- appState.getInvariantDeviceProfile().fillResIconDpi);
- }
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
- mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
- if (icon == null) {
- return null;
- }
- outObj[0] = icon;
- return icon;
- } else {
- return null;
- }
- }
-
- /**
* For apps icons and shortcut icons that have badges, this method creates a drawable that can
* later on be rendered on top of the layers for the badges. For app icons, work profile badges
* can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 794ab4e..5e41bb7 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,6 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.Preconditions;
@@ -133,39 +134,4 @@
return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
}
-
- /**
- * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
- */
- private static class ShiftedBitmapDrawable extends Drawable {
-
- private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
- private final Bitmap mBitmap;
- private final float mShiftX;
- private final float mShiftY;
-
- ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
- mBitmap = bitmap;
- mShiftX = shiftX;
- mShiftY = shiftY;
- }
-
- @Override
- public void draw(Canvas canvas) {
- canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
- }
-
- @Override
- public void setAlpha(int i) { }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
- }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 6fc81c9..7a14b36 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -74,6 +74,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
@@ -84,7 +85,7 @@
/**
* Represents a set of icons chosen by the user or generated by the system.
*/
-public class Folder extends AbstractFloatingView implements DragSource,
+public class Folder extends AbstractFloatingView implements ClipPathView, DragSource,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
@@ -1460,6 +1461,7 @@
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
* rounded rect.
*/
+ @Override
public void setClipPath(Path clipPath) {
mClipPath = clipPath;
invalidate();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 30dbe69..67495ea 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -189,6 +189,10 @@
return icon;
}
+ public void getPreviewBounds(Rect outBounds) {
+ mBackground.getBounds(outBounds);
+ }
+
public Folder getFolder() {
return mFolder;
}
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/folder/FolderShape.java
index 61db6ff..ec6078e 100644
--- a/src/com/android/launcher3/folder/FolderShape.java
+++ b/src/com/android/launcher3/folder/FolderShape.java
@@ -39,6 +39,7 @@
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.View;
import android.view.ViewOutlineProvider;
import com.android.launcher3.R;
@@ -46,6 +47,7 @@
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ClipPathView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,8 +76,8 @@
public abstract void addShape(Path path, float offsetX, float offsetY, float radius);
- public abstract Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed);
+ public abstract <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed);
@Nullable
public TypedValue getAttrValue(int attr) {
@@ -88,8 +90,8 @@
private static abstract class SimpleRectShape extends FolderShape {
@Override
- public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed) {
+ public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
return new RoundedRectRevealOutlineProvider(
getStartRadius(startRect), endRadius, startRect, endRect) {
@Override
@@ -121,8 +123,8 @@
Rect startRect, Rect endRect, float endRadius, Path outPath);
@Override
- public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed) {
+ public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
Path path = new Path();
AnimatorUpdateListener listener =
newUpdateListener(startRect, endRect, endRadius, path);
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index fd4774f..ac908f4 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -33,6 +33,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
+import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Shader;
import android.util.Property;
@@ -154,6 +155,14 @@
invalidate();
}
+ void getBounds(Rect outBounds) {
+ int top = basePreviewOffsetY;
+ int left = basePreviewOffsetX;
+ int right = left + previewSize;
+ int bottom = top + previewSize;
+ outBounds.set(left, top, right, bottom);
+ }
+
int getRadius() {
return previewSize / 2;
}
diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
new file mode 100644
index 0000000..52d45bb
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
@@ -0,0 +1,74 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
+ */
+public class ShiftedBitmapDrawable extends Drawable {
+
+ private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ private final Bitmap mBitmap;
+ private float mShiftX;
+ private float mShiftY;
+
+ public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
+ mBitmap = bitmap;
+ mShiftX = shiftX;
+ mShiftY = shiftY;
+ }
+
+ public float getShiftX() {
+ return mShiftX;
+ }
+
+ public float getShiftY() {
+ return mShiftY;
+ }
+
+ public void setShiftX(float shiftX) {
+ mShiftX = shiftX;
+ }
+
+ public void setShiftY(float shiftY) {
+ mShiftY = shiftY;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int i) { }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 6fac31e2..66f9dbf 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -16,9 +16,16 @@
package com.android.launcher3.graphics;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_USER_PRESENT;
+
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -89,6 +96,20 @@
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (ACTION_SCREEN_OFF.equals(action)) {
+ mAnimateScrimOnNextDraw = true;
+ } else if (ACTION_USER_PRESENT.equals(action)) {
+ // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
+ // the user unlocked and the Launcher is not in the foreground.
+ mAnimateScrimOnNextDraw = false;
+ }
+ }
+ };
+
private static final int DARK_SCRIM_COLOR = 0x55000000;
private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
private static final int ALPHA_MASK_HEIGHT_DP = 500;
@@ -204,11 +225,20 @@
public void onViewAttachedToWindow(View view) {
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
+
+ if (mTopScrim != null) {
+ IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
+ filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
+ mRoot.getContext().registerReceiver(mReceiver, filter);
+ }
}
@Override
public void onViewDetachedFromWindow(View view) {
mWallpaperColorInfo.removeOnChangeListener(this);
+ if (mTopScrim != null) {
+ mRoot.getContext().unregisterReceiver(mReceiver);
+ }
}
@Override
diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java
new file mode 100644
index 0000000..bfb3792
--- /dev/null
+++ b/src/com/android/launcher3/logging/EventLogArray.java
@@ -0,0 +1,136 @@
+/*
+ * 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.logging;
+
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * A utility class to record and log events. Events are stored in a fixed size array and old logs
+ * are purged as new events come.
+ */
+public class EventLogArray {
+
+ private static final int TYPE_ONE_OFF = 0;
+ private static final int TYPE_FLOAT = 1;
+ private static final int TYPE_INTEGER = 2;
+ private static final int TYPE_BOOL_TRUE = 3;
+ private static final int TYPE_BOOL_FALSE = 4;
+
+ private final String name;
+ private final EventEntry[] logs;
+ private int nextIndex;
+
+ public EventLogArray(String name, int size) {
+ this.name = name;
+ logs = new EventEntry[size];
+ nextIndex = 0;
+ }
+
+ public void addLog(String event) {
+ addLog(TYPE_ONE_OFF, event, 0);
+ }
+
+ public void addLog(String event, int extras) {
+ addLog(TYPE_INTEGER, event, extras);
+ }
+
+ public void addLog(String event, float extras) {
+ addLog(TYPE_FLOAT, event, extras);
+ }
+
+ public void addLog(String event, boolean extras) {
+ addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
+ }
+
+ private void addLog(int type, String event, float extras) {
+ // Merge the logs if its a duplicate
+ int last = (nextIndex + logs.length - 1) % logs.length;
+ int secondLast = (nextIndex + logs.length - 2) % logs.length;
+ if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
+ logs[last].update(type, event, extras);
+ logs[secondLast].duplicateCount++;
+ return;
+ }
+
+ if (logs[nextIndex] == null) {
+ logs[nextIndex] = new EventEntry();
+ }
+ logs[nextIndex].update(type, event, extras);
+ nextIndex = (nextIndex + 1) % logs.length;
+ }
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + name + " event history:");
+ SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US);
+ Date date = new Date();
+
+ for (int i = 0; i < logs.length; i++) {
+ EventEntry log = logs[(nextIndex + logs.length - i - 1) % logs.length];
+ if (log == null) {
+ continue;
+ }
+ date.setTime(log.time);
+
+ StringBuilder msg = new StringBuilder(prefix).append(sdf.format(date))
+ .append(log.event);
+ switch (log.type) {
+ case TYPE_BOOL_FALSE:
+ msg.append(": false");
+ break;
+ case TYPE_BOOL_TRUE:
+ msg.append(": true");
+ break;
+ case TYPE_FLOAT:
+ msg.append(": ").append(log.extras);
+ break;
+ case TYPE_INTEGER:
+ msg.append(": ").append((int) log.extras);
+ break;
+ default: // fall out
+ }
+ if (log.duplicateCount > 0) {
+ msg.append(" & ").append(log.duplicateCount).append(" similar events");
+ }
+ writer.println(msg);
+ }
+ }
+
+ private boolean isEntrySame(EventEntry entry, int type, String event) {
+ return entry != null && entry.type == type && entry.event.equals(event);
+ }
+
+ /** A single event entry. */
+ private static class EventEntry {
+
+ private int type;
+ private String event;
+ private float extras;
+ private long time;
+ private int duplicateCount;
+
+ public void update(int type, String event, float extras) {
+ this.type = type;
+ this.event = event;
+ this.extras = extras;
+ time = System.currentTimeMillis();
+ duplicateCount = 0;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 7275576..a27e2c2 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -366,13 +366,13 @@
ComponentName cn = intent.getComponent();
targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
- if (!Process.myUserHandle().equals(c.user)) {
+ if (allUsers.indexOfValue(c.user) < 0) {
if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Legacy shortcuts are only allowed for default user");
+ c.markDeleted("Legacy shortcuts are only allowed for current users");
continue;
} else if (c.restoreFlag != 0) {
// Don't restore items for other profiles.
- c.markDeleted("Restore from managed profile not supported");
+ c.markDeleted("Restore from other profiles not supported");
continue;
}
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index bcca4d8..040b5e5 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -18,10 +18,15 @@
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LongSparseArray;
+import android.util.SparseLongArray;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
@@ -48,10 +53,10 @@
private static final String INFO_COLUMN_NAME = "name";
private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
- public static boolean performRestore(DatabaseHelper helper) {
+ public static boolean performRestore(DatabaseHelper helper, BackupManager backupManager) {
SQLiteDatabase db = helper.getWritableDatabase();
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
- new RestoreDbTask().sanitizeDB(helper, db);
+ new RestoreDbTask().sanitizeDB(helper, db, backupManager);
t.commit();
return true;
} catch (Exception e) {
@@ -62,20 +67,44 @@
/**
* Makes the following changes in the provider DB.
- * 1. Removes all entries belonging to a managed profile as managed profiles
- * cannot be restored.
+ * 1. Removes all entries belonging to any profiles that were not restored.
* 2. Marks all entries as restored. The flags are updated during first load or as
* the restored apps get installed.
- * 3. If the user serial for primary profile is different than that of the previous device,
- * update the entries to the new profile id.
+ * 3. If the user serial for any restored profile is different than that of the previous
+ * device, update the entries to the new profile id.
*/
- private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
+ private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
+ throws Exception {
+ // Primary user ids
+ long myProfileId = helper.getDefaultUserSerial();
long oldProfileId = getDefaultProfileId(db);
- // Delete all entries which do not belong to the main user
- int itemsDeleted = db.delete(
- Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
+ LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
+ LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
+ + 1);
+
+ // Build mapping of restored profile ids to their new profile ids.
+ profileMapping.put(oldProfileId, myProfileId);
+ for (int i = oldManagedProfileIds.size() - 1; i >= 0; --i) {
+ long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
+ UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
+ if (user != null) {
+ long newManagedProfileId = helper.getSerialNumberForUser(user);
+ profileMapping.put(oldManagedProfileId, newManagedProfileId);
+ }
+ }
+
+ // Delete all entries which do not belong to any restored profile(s).
+ int numProfiles = profileMapping.size();
+ String[] profileIds = new String[numProfiles];
+ profileIds[0] = Long.toString(oldProfileId);
+ StringBuilder whereClause = new StringBuilder("profileId != ?");
+ for (int i = profileMapping.size() - 1; i >= 1; --i) {
+ whereClause.append(" AND profileId != ?");
+ profileIds[i] = Long.toString(profileMapping.keyAt(i));
+ }
+ int itemsDeleted = db.delete(Favorites.TABLE_NAME, whereClause.toString(), profileIds);
if (itemsDeleted > 0) {
- FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
+ FileLog.d(TAG, itemsDeleted + " items from unrestored user(s) were deleted");
}
// Mark all items as restored.
@@ -85,7 +114,7 @@
| (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
db.update(Favorites.TABLE_NAME, values, null, null);
- // Mark widgets with appropriate restore flag
+ // Mark widgets with appropriate restore flag.
values.put(Favorites.RESTORED, LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
@@ -93,21 +122,46 @@
db.update(Favorites.TABLE_NAME, values, "itemType = ?",
new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
- long myProfileId = helper.getDefaultUserSerial();
+ // Migrate ids. To avoid any overlap, we initially move conflicting ids to a temp location.
+ // Using Long.MIN_VALUE since profile ids can not be negative, so there will be no overlap.
+ final long tempLocationOffset = Long.MIN_VALUE;
+ SparseLongArray tempMigratedIds = new SparseLongArray(profileMapping.size());
+ int numTempMigrations = 0;
+ for (int i = profileMapping.size() - 1; i >= 0; --i) {
+ long oldId = profileMapping.keyAt(i);
+ long newId = profileMapping.valueAt(i);
+
+ if (oldId != newId) {
+ if (profileMapping.indexOfKey(newId) >= 0) {
+ tempMigratedIds.put(numTempMigrations, newId);
+ numTempMigrations++;
+ newId = tempLocationOffset + newId;
+ }
+ migrateProfileId(db, oldId, newId);
+ }
+ }
+
+ // Migrate ids from their temporary id to their actual final id.
+ for (int i = tempMigratedIds.size() - 1; i >= 0; --i) {
+ long newId = tempMigratedIds.valueAt(i);
+ migrateProfileId(db, tempLocationOffset + newId, newId);
+ }
+
if (myProfileId != oldProfileId) {
- FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
- migrateProfileId(db, myProfileId);
+ changeDefaultColumn(db, myProfileId);
}
}
/**
- * Updates profile id of all entries and changes the default value for the column.
+ * Updates profile id of all entries from {@param oldProfileId} to {@param newProfileId}.
*/
- protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
+ protected void migrateProfileId(SQLiteDatabase db, long oldProfileId, long newProfileId) {
+ FileLog.d(TAG, "Changing profile user id from " + oldProfileId + " to " + newProfileId);
// Update existing entries.
ContentValues values = new ContentValues();
values.put(Favorites.PROFILE_ID, newProfileId);
- db.update(Favorites.TABLE_NAME, values, null, null);
+ db.update(Favorites.TABLE_NAME, values, "profileId = ?",
+ new String[]{Long.toString(oldProfileId)});
// Change default value of the column.
db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
@@ -116,6 +170,43 @@
dropTable(db, "favorites_old");
}
+
+ /**
+ * Changes the default value for the column.
+ */
+ protected void changeDefaultColumn(SQLiteDatabase db, long newProfileId) {
+ db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
+ Favorites.addTableToDb(db, newProfileId, false);
+ db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
+ dropTable(db, "favorites_old");
+ }
+
+ /**
+ * Returns a list of the managed profile id(s) used in the favorites table of the provided db.
+ */
+ private LongSparseArray<Long> getManagedProfileIds(SQLiteDatabase db, long defaultProfileId) {
+ LongSparseArray<Long> ids = new LongSparseArray<>();
+ try (Cursor c = db.rawQuery("SELECT profileId from favorites WHERE profileId != ? "
+ + "GROUP BY profileId", new String[] {Long.toString(defaultProfileId)})){
+ while (c.moveToNext()) {
+ ids.put(c.getLong(c.getColumnIndex(Favorites.PROFILE_ID)), null);
+ }
+ }
+ return ids;
+ }
+
+ /**
+ * Returns a UserHandle of a restored managed profile with the given serial number, or null
+ * if none found.
+ */
+ private UserHandle getUserForAncestralSerialNumber(BackupManager backupManager,
+ long ancestralSerialNumber) {
+ if (Build.VERSION.SDK_INT < 29) {
+ return null;
+ }
+ return backupManager.getUserForAncestralSerialNumber(ancestralSerialNumber);
+ }
+
/**
* Returns the profile id used in the favorites table of the provided db.
*/
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index d326ff3..c6370c5 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -36,7 +36,6 @@
public abstract class InternalStateHandler extends Binder {
public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
- public static final String EXTRA_FROM_HOME_KEY = "android.intent.extra.FROM_HOME_KEY";
private static final Scheduler sScheduler = new Scheduler();
@@ -77,10 +76,6 @@
Launcher launcher, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
boolean result = false;
if (intent != null && intent.getExtras() != null) {
- // If we know that this the intent comes from pressing Home, defer to the default
- // processing.
- if (intent.hasExtra(EXTRA_FROM_HOME_KEY)) return false;
-
IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER);
if (stateBinder instanceof InternalStateHandler) {
InternalStateHandler handler = (InternalStateHandler) stateBinder;
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 35f7f88..fcace98 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -73,6 +73,11 @@
}
@Override
+ public float[] getHotseatScaleAndTranslation(Launcher launcher) {
+ return new float[] {1, 0, 0};
+ }
+
+ @Override
public void onStateEnabled(Launcher launcher) {
Workspace ws = launcher.getWorkspace();
ws.showPageIndicatorAtCurrentScroll();
diff --git a/src/com/android/launcher3/views/ClipPathView.java b/src/com/android/launcher3/views/ClipPathView.java
new file mode 100644
index 0000000..2152e1d
--- /dev/null
+++ b/src/com/android/launcher3/views/ClipPathView.java
@@ -0,0 +1,27 @@
+/*
+ * 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.views;
+
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * Alternative to using {@link View#getClipToOutline()} as it only works with derivatives of
+ * rounded rect.
+ */
+public interface ClipPathView {
+ void setClipPath(Path clipPath);
+}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 07318c9..ec0462b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -16,41 +16,91 @@
package com.android.launcher3.views;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
+import com.android.launcher3.icons.LauncherIcons;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
-public class FloatingIconView extends View implements Animator.AnimatorListener {
+
+public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
+
+ private static final Rect sTmpRect = new Rect();
private Runnable mStartRunnable;
private Runnable mEndRunnable;
- public FloatingIconView(Context context) {
- super(context);
- }
+ private Drawable mDrawable;
+ private int mOriginalHeight;
+ private final int mBlurSizeOutline;
- public void setRunnables(Runnable startRunnable, Runnable endRunnable) {
- mStartRunnable = startRunnable;
- mEndRunnable = endRunnable;
+ private boolean mIsAdaptiveIcon = false;
+
+ private @Nullable Drawable mForeground;
+ private @Nullable Drawable mBackground;
+ private ValueAnimator mRevealAnimator;
+ private final Rect mStartRevealRect = new Rect();
+ private final Rect mEndRevealRect = new Rect();
+ private Path mClipPath;
+ protected final Rect mOutline = new Rect();
+ private final float mTaskCornerRadius;
+
+ private final Rect mFinalDrawableBounds = new Rect();
+ private final Rect mBgDrawableBounds = new Rect();
+ private float mBgDrawableStartScale = 1f;
+
+ private FloatingIconView(Context context) {
+ super(context);
+
+ mBlurSizeOutline = context.getResources().getDimensionPixelSize(
+ R.dimen.blur_size_medium_outline);
+
+ mTaskCornerRadius = 0; // TODO
}
/**
* Positions this view to match the size and location of {@param rect}.
+ *
+ * @param alpha The alpha to set this view.
+ * @param progress A value from [0, 1] that represents the animation progress.
+ * @param windowAlphaThreshold The value at which the window alpha is 0.
*/
- public void update(RectF rect, float alpha) {
+ public void update(RectF rect, float alpha, float progress, float windowAlphaThreshold) {
setAlpha(alpha);
LayoutParams lp = (LayoutParams) getLayoutParams();
@@ -59,13 +109,44 @@
setTranslationX(dX);
setTranslationY(dY);
- float scaleX = rect.width() / (float) getWidth();
- float scaleY = rect.height() / (float) getHeight();
- float scale = Math.min(scaleX, scaleY);
+ float scaleX = rect.width() / (float) lp.width;
+ float scaleY = rect.height() / (float) lp.height;
+ float scale = mIsAdaptiveIcon ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
+
+ // Wait until the window is no longer visible before morphing the icon into its final shape.
+ float shapeRevealProgress = Utilities.mapToRange(Math.max(windowAlphaThreshold, progress),
+ windowAlphaThreshold, 1f, 0f, 1, Interpolators.LINEAR);
+ if (mIsAdaptiveIcon && shapeRevealProgress > 0) {
+ if (mRevealAnimator == null) {
+ mEndRevealRect.set(mOutline);
+ // We play the reveal animation in reverse so that we end with the icon shape.
+ mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
+ mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, true);
+ mRevealAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRevealAnimator = null;
+ }
+ });
+ mRevealAnimator.start();
+ // We pause here so we can set the current fraction ourselves.
+ mRevealAnimator.pause();
+ }
+
+ float bgScale = shapeRevealProgress + mBgDrawableStartScale * (1 - shapeRevealProgress);
+ setBackgroundDrawableBounds(bgScale);
+
+ mRevealAnimator.setCurrentFraction(shapeRevealProgress);
+ if (Float.compare(shapeRevealProgress, 1f) >= 0f) {
+ mRevealAnimator.end();
+ }
+ }
+ invalidate();
+ invalidateOutline();
}
@Override
@@ -82,25 +163,17 @@
}
}
- @Override
- public void onAnimationCancel(Animator animator) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animator) {
- }
-
/**
* Sets the size and position of this view to match {@param v}.
*
* @param v The view to copy
- * @param hideOriginal If true, it will hide {@param v} while this view is visible.
* @param positionOut Rect that will hold the size and position of v.
*/
- public void matchPositionOf(Launcher launcher, View v, boolean hideOriginal, Rect positionOut) {
+ private void matchPositionOf(Launcher launcher, View v, Rect positionOut) {
Utilities.getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
lp.ignoreInsets = true;
+ mOriginalHeight = lp.height;
// Position the floating view exactly on top of the original
lp.leftMargin = positionOut.left;
@@ -110,29 +183,191 @@
// animation frame.
layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ lp.height);
+ }
- if (v instanceof BubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
- // Create a copy of the app icon
- setBackground(DrawableFactory.INSTANCE.get(launcher)
- .newIcon(v.getContext(), (ItemInfoWithIcon) v.getTag()));
+ @WorkerThread
+ private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs,
+ float aspectRatio) {
+ final LayoutParams lp = (LayoutParams) getLayoutParams();
+
+ boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+ if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
+ // Similar to DragView, we simply use the BubbleTextView icon here.
+ mDrawable = ((BubbleTextView) v).getIcon();
+ }
+ if (mDrawable == null) {
+ mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
+ useDrawableAsIs, new Object[1]);
+ }
+
+ if (supportsAdaptiveIcons && mDrawable instanceof AdaptiveIconDrawable) {
+ mIsAdaptiveIcon = true;
+
+ AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) mDrawable;
+ Drawable background = adaptiveIcon.getBackground();
+ if (background == null) {
+ background = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mBackground = background;
+ Drawable foreground = adaptiveIcon.getForeground();
+ if (foreground == null) {
+ foreground = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mForeground = foreground;
+
+ int offset = getOffsetForAdaptiveIconBounds();
+ mFinalDrawableBounds.set(offset, offset, lp.width - offset, mOriginalHeight - offset);
+ if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
+ ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
+ ((FolderIcon) v).getPreviewBounds(sTmpRect);
+ sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
+ sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
+ }
+ mForeground.setBounds(mFinalDrawableBounds);
+ mBackground.setBounds(mFinalDrawableBounds);
+
+ int blurMargin = mBlurSizeOutline / 2;
+ mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
+ mOriginalHeight - blurMargin);
+
+ if (aspectRatio > 0) {
+ lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ + lp.height);
+ }
+ mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
+ setBackgroundDrawableBounds(mBgDrawableStartScale);
+
+ // Set up outline
+ mOutline.set(0, 0, lp.width, lp.height);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(mOutline, mTaskCornerRadius);
+ }
+ });
+ setClipToOutline(true);
+ } else {
+ setBackground(mDrawable);
+ }
+
+ new Handler(Looper.getMainLooper()).post(() -> {
+ invalidate();
+ invalidateOutline();
+ });
+ }
+
+ private void setBackgroundDrawableBounds(float scale) {
+ mBgDrawableBounds.set(mFinalDrawableBounds);
+ Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale);
+ // Since the drawable is at the top of the view, we need to offset to keep it centered.
+ mBgDrawableBounds.offsetTo(mBgDrawableBounds.left,
+ (int) (mFinalDrawableBounds.top * scale));
+ mBackground.setBounds(mBgDrawableBounds);
+ }
+
+ private int getOffsetForAdaptiveIconBounds() {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
+ !(mDrawable instanceof AdaptiveIconDrawable)) {
+ return 0;
+ }
+
+ final LayoutParams lp = (LayoutParams) getLayoutParams();
+ Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline);
+ bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
+
+ try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) {
+ Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(mDrawable, null));
+ }
+
+ bounds.inset(
+ (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+ (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+ );
+
+ return bounds.left;
+ }
+
+ @Override
+ public void setClipPath(Path clipPath) {
+ mClipPath = clipPath;
+ invalidate();
+ }
+
+ private void drawAdaptiveIconIfExists(Canvas canvas) {
+ if (mBackground != null) {
+ mBackground.draw(canvas);
+ }
+ if (mForeground != null) {
+ mForeground.draw(canvas);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mClipPath == null) {
+ super.draw(canvas);
+ drawAdaptiveIconIfExists(canvas);
+ } else {
+ int count = canvas.save();
+ canvas.clipPath(mClipPath);
+ super.draw(canvas);
+ drawAdaptiveIconIfExists(canvas);
+ canvas.restoreToCount(count);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {}
+
+ /**
+ * Creates a floating icon view for {@param originalView}.
+ *
+ * @param originalView The view to copy
+ * @param hideOriginal If true, it will hide {@param originalView} while this view is visible.
+ * @param useDrawableAsIs If true, we do not separate the foreground/background of adaptive
+ * icons. TODO(b/122843905): We can remove this once app opening uses new animation.
+ * @param aspectRatio If >= 0, we will use this aspect ratio for the initial adaptive icon size.
+ * @param positionOut Rect that will hold the size and position of v.
+ */
+ public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
+ boolean hideOriginal, boolean useDrawableAsIs, float aspectRatio, Rect positionOut,
+ FloatingIconView recycle) {
+ FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher);
+
+ // Match the position of the original view.
+ view.matchPositionOf(launcher, originalView, positionOut);
+
+ // Get the drawable on the background thread
+ // Must be called after matchPositionOf so that we know what size to load.
+ if (originalView.getTag() instanceof ItemInfo) {
+ new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
+ view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(),
+ useDrawableAsIs, aspectRatio);
+ });
}
// We need to add it to the overlay, but keep it invisible until animation starts..
final DragLayer dragLayer = launcher.getDragLayer();
- setVisibility(INVISIBLE);
- ((ViewGroup) dragLayer.getParent()).getOverlay().add(this);
+ view.setVisibility(INVISIBLE);
+ ((ViewGroup) dragLayer.getParent()).getOverlay().add(view);
- setRunnables(() -> {
- setVisibility(VISIBLE);
- if (hideOriginal) {
- v.setVisibility(INVISIBLE);
- }
- },
- () -> {
- ((ViewGroup) dragLayer.getParent()).getOverlay().remove(this);
- if (hideOriginal) {
- v.setVisibility(VISIBLE);
- }
- });
+ view.mStartRunnable = () -> {
+ view.setVisibility(VISIBLE);
+ if (hideOriginal) {
+ originalView.setVisibility(INVISIBLE);
+ }
+ };
+ view.mEndRunnable = () -> {
+ ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);
+ if (hideOriginal) {
+ originalView.setVisibility(VISIBLE);
+ }
+ };
+ return view;
}
}
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index babb731..80facc6 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -41,11 +41,27 @@
// Verify item add
assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
- new RestoreDbTask().migrateProfileId(db, 33);
+ new RestoreDbTask().migrateProfileId(db, 42, 33);
// verify data migrated
assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
assertEquals(5, getCount(db, "select * from favorites where profileId = 33"));
+ }
+
+ @Test
+ public void testChangeDefaultColumn() throws Exception {
+ SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ // Add some dummy data
+ for (int i = 0; i < 5; i++) {
+ ContentValues values = new ContentValues();
+ values.put(Favorites._ID, i);
+ values.put(Favorites.TITLE, "item " + i);
+ db.insert(Favorites.TABLE_NAME, null, values);
+ }
+ // Verify item add
+ assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
+
+ new RestoreDbTask().changeDefaultColumn(db, 33);
// Verify default value changed
ContentValues values = new ContentValues();
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 02f5502..dd768fd 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -98,6 +98,10 @@
}
if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
mLauncher = new LauncherInstrumentation(instrumentation);
+ try {
+ mDevice.executeShellCommand("settings delete secure assistant");
+ } catch (IOException e) {
+ }
}
@Rule
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 82ea8be..1353a23 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,7 +29,7 @@
public class AllApps extends LauncherInstrumentation.VisibleContainer {
private static final int MAX_SCROLL_ATTEMPTS = 40;
private static final int MIN_INTERACT_SIZE = 100;
- private static final int FLING_SPEED = 12000;
+ private static final int FLING_SPEED = 3000;
private final int mHeight;
@@ -120,7 +120,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
- allAppsContainer.fling(Direction.DOWN, FLING_SPEED);
+ allAppsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
verifyActiveContainer();
}
@@ -131,7 +131,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
- allAppsContainer.fling(Direction.UP, FLING_SPEED);
+ allAppsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 0ff3070..6e92dad 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -28,7 +28,7 @@
* Common overview pane for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
- private static final int DEFAULT_FLING_SPEED = 15000;
+ private static final int FLING_SPEED = 1500;
private static final int FLINGS_FOR_DISMISS_LIMIT = 5;
BaseOverview(LauncherInstrumentation launcher) {
@@ -46,7 +46,7 @@
public void flingForward() {
final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingForward before fling");
- overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED);
+ overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
}
@@ -73,7 +73,7 @@
public void flingBackward() {
final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingBackward before fling");
- overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
+ overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 7473189..93b4cc6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -93,8 +93,6 @@
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
private final UiDevice mDevice;
- private final boolean mSwipeUpEnabled;
- private Boolean mSwipeUpEnabledOverride = null;
private final Instrumentation mInstrumentation;
private int mExpectedRotation = Surface.ROTATION_0;
@@ -104,13 +102,6 @@
public LauncherInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
mDevice = UiDevice.getInstance(instrumentation);
- final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
- mSwipeUpEnabled = SwipeUpSetting.isSwipeUpSettingAvailable() ?
- Settings.Secure.getInt(
- instrumentation.getTargetContext().getContentResolver(),
- SWIPE_UP_SETTING_NAME,
- swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
- swipeUpEnabledDefaultValue;
// Launcher should run in test harness so that custom accessibility protocol between
// Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -119,17 +110,18 @@
TestHelpers.isInLauncherProcess() || ActivityManager.isRunningInTestHarness());
}
- // Used only by TaplTests.
- public void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride) {
- mSwipeUpEnabledOverride = swipeUpEnabledOverride;
- }
-
void setActiveContainer(VisibleContainer container) {
sActiveContainer = new WeakReference<>(container);
}
public boolean isSwipeUpEnabled() {
- return mSwipeUpEnabledOverride != null ? mSwipeUpEnabledOverride : mSwipeUpEnabled;
+ final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
+ return SwipeUpSetting.isSwipeUpSettingAvailable() ?
+ Settings.Secure.getInt(
+ mInstrumentation.getTargetContext().getContentResolver(),
+ SWIPE_UP_SETTING_NAME,
+ swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
+ swipeUpEnabledDefaultValue;
}
static void log(String message) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 77555a3..89affd1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -23,7 +23,7 @@
* All widgets container.
*/
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
- private static final int FLING_SPEED = 6000;
+ private static final int FLING_SPEED = 1500;
Widgets(LauncherInstrumentation launcher) {
super(launcher);
@@ -36,7 +36,7 @@
public void flingForward() {
final UiObject2 widgetsContainer = verifyActiveContainer();
widgetsContainer.setGestureMargin(100);
- widgetsContainer.fling(Direction.DOWN, FLING_SPEED);
+ widgetsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
verifyActiveContainer();
}
@@ -46,7 +46,7 @@
public void flingBackward() {
final UiObject2 widgetsContainer = verifyActiveContainer();
widgetsContainer.setGestureMargin(100);
- widgetsContainer.fling(Direction.UP, FLING_SPEED);
+ widgetsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
}