Merging from ub-launcher3-master @ build 6269120 am: 8f03c5c1ce am: 8df766f1ba

Change-Id: I26b7956629d4afe63fc1085351472a024880533a
This commit is contained in:
Automerger Merge Worker
2020-03-06 18:03:04 +00:00
101 changed files with 3246 additions and 683 deletions
+1
View File
@@ -71,6 +71,7 @@
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
android:backupInForeground="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher_home"
+19 -2
View File
@@ -8,9 +8,26 @@ adamcohen@google.com
hyunyoungs@google.com
mrcasey@google.com
sunnygoyal@google.com
awickham@google.com
twickham@google.com
winsonc@google.com
zakcohen@google.com
santie@google.com
vadimt@google.com
mett@google.com
jonmiranda@google.com
pinyaoting@google.com
sfufa@google.com
gwasserman@google.com
jamesoleary@google.com
joshtrask@google.com
mrenouf@google.com
mkephart@google.com
hwwang@google.com
tracyzhou@google.com
peanutbutter@google.com
xuqiu@google.com
sreyasr@google.com
per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
+1
View File
@@ -155,6 +155,7 @@ enum ControlType {
OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
SELECT_MODE_CLOSE_BUTTON = 27;
SELECT_MODE_ITEM = 28;
}
enum TipType {
+1 -1
View File
@@ -52,7 +52,7 @@
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:taskAffinity="${packageName}.launcher"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+1
View File
@@ -93,6 +93,7 @@
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:taskAffinity="${packageName}.launcher"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
@@ -28,4 +28,9 @@
android:clipToPadding="false"
android:outlineProvider="none"
android:theme="@style/HomeScreenElementTheme" />
<include
android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_holder" />
</com.android.quickstep.fallback.RecentsRootView>
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,17 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.InsettableFrameLayout
<com.android.quickstep.views.LauncherRecentsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.android.quickstep.views.LauncherRecentsView
android:id="@+id/overview_panel_recents"
android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:accessibilityPaneTitle="@string/accessibility_recent_apps"
android:visibility="invisible" />
</com.android.launcher3.InsettableFrameLayout>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:accessibilityPaneTitle="@string/accessibility_recent_apps"
android:clipChildren="false"
android:clipToPadding="false"
android:theme="@style/HomeScreenElementTheme"
android:visibility="invisible" />
@@ -35,25 +35,27 @@
<TextView
style="@style/TextHeadline"
android:id="@+id/hotseat_edu_heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding"
android:text="@string/hotseat_migrate_title"
android:text="@string/hotseat_edu_title_migrate"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:id="@+id/hotseat_edu_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:layout_marginBottom="18dp"
android:fontFamily="roboto-medium"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding"
android:text="@string/hotseat_migrate_message"
android:text="@string/hotseat_edu_message_migrate"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="16sp" />
@@ -83,7 +85,7 @@
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="?android:attr/selectableItemBackground"
android:text="@string/hotseat_migrate_accept"
android:text="@string/hotseat_edu_accept"
android:textAlignment="textEnd"
android:textColor="@android:color/white" />
@@ -91,7 +93,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/no_thanks"
android:text="@string/hotseat_migrate_dismiss"
android:text="@string/hotseat_edu_dismiss"
android:layout_gravity="start"
android:background="?android:attr/selectableItemBackground"
android:textColor="@android:color/white" />
@@ -23,10 +23,4 @@
<!-- Minimum distance to swipe to trigger accessibility gesture -->
<dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
<!-- Swipe up to home related -->
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_y_overshoot">10dp</dimen>
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
</resources>
@@ -16,7 +16,6 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -39,6 +38,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.FloatProperty;
import android.view.View;
import androidx.annotation.NonNull;
@@ -50,6 +50,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -88,7 +89,8 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
AppWindowAnimationHelper helper = new AppWindowAnimationHelper(mLauncher);
AppWindowAnimationHelper helper =
new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher);
anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets,
wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
@@ -197,7 +199,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
case INDEX_RECENTS_TRANSLATE_X_ANIM:
return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
PagedOrientationHandler orientationHandler =
((RecentsView)mLauncher.getOverviewPanel()).getPagedViewOrientedState()
.getOrientationHandler();
FloatProperty<View> translate = orientationHandler.getPrimaryViewTranslate();
return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), translate)
.setDampingRatio(0.8f)
.setStiffness(250)
.setValues(values)
@@ -27,11 +27,15 @@ import android.view.ViewGroup;
import androidx.core.app.NotificationCompat;
import com.android.launcher3.CellLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.Themes;
@@ -61,16 +65,25 @@ public class HotseatEduController {
mNotification = createNotification();
}
void migrate() {
boolean migrate() {
Workspace workspace = mLauncher.getWorkspace();
CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID);
int toPage = Workspace.FIRST_SCREEN_ID;
int toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount());
toRow = 0;
} else if (!firstScreen.makeSpaceForHotseatMigration(true)) {
return false;
}
ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets();
int workspacePageCount = mLauncher.getWorkspace().getPageCount();
for (int i = 0; i < hotseatVG.getChildCount(); i++) {
View child = hotseatVG.getChildAt(i);
ItemInfo tag = (ItemInfo) child.getTag();
mLauncher.getModelWriter().moveItemInDatabase(tag,
LauncherSettings.Favorites.CONTAINER_DESKTOP, workspacePageCount, tag.screenId,
0);
LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow);
}
return true;
}
void removeNotification() {
@@ -93,7 +106,7 @@ public class HotseatEduController {
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
CharSequence name = mLauncher.getString(R.string.hotseat_migrate_title);
CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name,
importance);
@@ -104,8 +117,8 @@ public class HotseatEduController {
Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass());
intent = new NotificationHandler().addToIntent(intent);
CharSequence name = mLauncher.getString(R.string.hotseat_migrate_prompt_title);
String description = mLauncher.getString(R.string.hotseat_migrate_prompt_content);
CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
String description = mLauncher.getString(R.string.hotseat_edu_prompt_content);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher,
NOTIFICATION_CHANNEL_ID)
.setContentTitle(name)
@@ -26,6 +26,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.CellLayout;
@@ -34,7 +35,9 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -49,12 +52,24 @@ import java.util.List;
public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
private static final int DEFAULT_CLOSE_DURATION = 200;
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
// We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen
private static final int SAME_PAGE_MAX_ROWS = 2;
private static final int MIGRATE_SAME_PAGE = 0;
private static final int MIGRATE_NEW_PAGE = 1;
private static final int MIGRATE_NO_MIGRATE = 2;
public static boolean shown = false;
private final Rect mInsets = new Rect();
private View mHotseatWrapper;
private CellLayout mSampleHotseat;
private TextView mEduHeading;
private TextView mEduContent;
private Button mDismissBtn;
private int mMigrationMode = MIGRATE_SAME_PAGE;
public void setHotseatEduController(HotseatEduController hotseatEduController) {
mHotseatEduController = hotseatEduController;
@@ -78,6 +93,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
super.onFinishInflate();
mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
mSampleHotseat = findViewById(R.id.sample_prediction);
mEduHeading = findViewById(R.id.hotseat_edu_heading);
mEduContent = findViewById(R.id.hotseat_edu_content);
DeviceProfile grid = mLauncher.getDeviceProfile();
Rect padding = grid.getHotseatLayoutPadding();
@@ -87,24 +104,27 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
Button turnOnBtn = findViewById(R.id.turn_predictions_on);
turnOnBtn.setOnClickListener(this::onMigrate);
turnOnBtn.setOnClickListener(this::onAccept);
Button learnMoreBtn = findViewById(R.id.no_thanks);
learnMoreBtn.setOnClickListener(this::onKeepDefault);
mDismissBtn = findViewById(R.id.no_thanks);
mDismissBtn.setOnClickListener(this::onDismiss);
}
private void onMigrate(View v) {
if (mHotseatEduController == null) return;
private void onAccept(View v) {
if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
onDismiss(v);
return;
}
handleClose(true);
mHotseatEduController.migrate();
mHotseatEduController.finishOnboarding();
logUserAction(true);
Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
}
private void onKeepDefault(View v) {
if (mHotseatEduController == null) return;
private void onDismiss(View v) {
Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
mHotseatEduController.finishOnboarding();
logUserAction(false);
@@ -148,6 +168,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
: HYBRID_HOTSEAT_CANCELED;
// encoding migration type on pageIndex
target.pageIndex = mMigrationMode;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
@@ -161,6 +183,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -183,17 +206,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
handleClose(false);
}
/**
* Opens User education dialog with a list of suggested apps
*/
public void show(List<WorkspaceItemInfo> predictions) {
if (getParent() != null
|| predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
return;
}
attachToContainer();
logOnBoardingSeen();
animateOpen();
@Override
protected int getScrimColor(Context context) {
return FINAL_SCRIM_BG_COLOR;
}
private void populatePreview(List<WorkspaceItemInfo> predictions) {
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
WorkspaceItemInfo info = predictions.get(i);
PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
@@ -204,6 +222,43 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
}
}
@Override
protected void attachToContainer() {
super.attachToContainer();
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
mMigrationMode = MIGRATE_NEW_PAGE;
return;
}
CellLayout page = mLauncher.getWorkspace().getScreenWithId(
WorkspaceLayoutManager.FIRST_SCREEN_ID);
int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns
+ (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage
|| !page.makeSpaceForHotseatMigration(false)) {
mMigrationMode = MIGRATE_NO_MIGRATE;
mEduContent.setText(R.string.hotseat_edu_message_no_migrate);
mEduHeading.setText(R.string.hotseat_edu_title_no_migrate);
mDismissBtn.setVisibility(GONE);
}
}
/**
* Opens User education dialog with a list of suggested apps
*/
public void show(List<WorkspaceItemInfo> predictions) {
if (getParent() != null
|| predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
|| mHotseatEduController == null) {
return;
}
attachToContainer();
logOnBoardingSeen();
animateOpen();
populatePreview(predictions);
}
/**
* Factory method for HotseatPredictionUserEdu dialog
*/
@@ -80,10 +80,10 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
}
@Override
public void onDragStart(boolean start) {
public void onDragStart(boolean start, float startDisplacement) {
mMotionPauseDetector.clear();
super.onDragStart(start);
super.onDragStart(start, startDisplacement);
if (handlingOverviewAnim()) {
mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
@@ -126,7 +126,7 @@ public class NavBarToHomeTouchController implements TouchController,
}
@Override
public void onDragStart(boolean start) {
public void onDragStart(boolean start, float startDisplacement) {
initCurrentAnimation();
}
@@ -94,8 +94,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
}
@Override
public void onDragStart(boolean start) {
super.onDragStart(start);
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
mReachedOverview = false;
}
@@ -116,12 +116,13 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
mLauncher = launcher;
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
mShelfPeekAnim = mLauncher.getShelfPeekAnim();
mRecentsView = mLauncher.getOverviewPanel();
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile());
mYRange = LayoutUtils.getShelfTrackingDistance(
mLauncher, mLauncher.getDeviceProfile());
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mRecentsView = mLauncher.getOverviewPanel();
}
@Override
@@ -92,8 +92,8 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
}
@Override
public void onDragStart(boolean start) {
super.onDragStart(start);
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
ActivityManagerWrapper.getInstance()
@@ -36,6 +36,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
@@ -77,7 +78,9 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
public TaskViewTouchController(T activity) {
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL);
SingleAxisSwipeDetector.Direction dir =
mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection();
mDetector = new SingleAxisSwipeDetector(activity, this, dir);
}
private boolean canInterceptTouch() {
@@ -105,6 +108,10 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)
&& mCurrentAnimation == null) {
clearState();
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = !canInterceptTouch();
if (mNoIntercept) {
@@ -125,6 +132,11 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
TaskView view = mRecentsView.getTaskViewAt(i);
if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
.isEventOverView(view, ev)) {
// Disable swiping up and down if the task overlay is modal.
if (view.isTaskOverlayModal()) {
mTaskBeingDragged = null;
break;
}
mTaskBeingDragged = view;
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
// Don't allow swipe down to open if we don't support swipe up
@@ -181,15 +193,18 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
mPendingAnimation = null;
}
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
mCurrentAnimationIsGoingUp = goingUp;
BaseDragLayer dl = mActivity.getDragLayer();
long maxDuration = (long) (2 * dl.getHeight());
final int secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl);
long maxDuration = (long) (2 * secondaryLayerDimension);
int verticalFactor = -orientationHandler.getTaskDismissDirectionFactor();
int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged);
if (goingUp) {
mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration);
mEndDisplacement = -mTaskBeingDragged.getHeight();
mEndDisplacement = -secondaryTaskDimension;
} else {
mPendingAnimation = mRecentsView.createTaskLaunchAnimation(
mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN);
@@ -198,6 +213,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
mEndDisplacement = dl.getHeight() - mTempCords[1];
}
mEndDisplacement *= verticalFactor;
if (mCurrentAnimation != null) {
mCurrentAnimation.setOnCancelRunnable(null);
@@ -211,9 +227,10 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
}
@Override
public void onDragStart(boolean start) {
public void onDragStart(boolean start, float startDisplacement) {
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
if (mCurrentAnimation == null) {
reInitAnimationController(mDetector.wasInitialTouchPositive());
reInitAnimationController(orientationHandler.isGoingUp(startDisplacement));
mDisplacementShift = 0;
} else {
mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
@@ -224,9 +241,10 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
@Override
public boolean onDrag(float displacement) {
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
float totalDisplacement = displacement + mDisplacementShift;
boolean isGoingUp =
totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
boolean isGoingUp = totalDisplacement == 0 ? mCurrentAnimationIsGoingUp :
orientationHandler.isGoingUp(totalDisplacement);
if (isGoingUp != mCurrentAnimationIsGoingUp) {
reInitAnimationController(isGoingUp);
mFlingBlockCheck.blockFling();
@@ -253,11 +271,12 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
if (blockedFling) {
fling = false;
}
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
if (fling) {
logAction = Touch.FLING;
boolean goingUp = velocity < 0;
boolean goingUp = orientationHandler.isGoingUp(velocity);
goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
} else {
logAction = Touch.SWIPE;
@@ -125,7 +125,8 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
return anim;
}
final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(mActivity);
final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(
mRecentsView.getPagedViewOrientedState(), mActivity);
// At this point, the activity is already started and laid-out. Get the home-bounds
// relative to the screen using the rootView of the activity.
@@ -17,7 +17,6 @@ package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
@@ -32,6 +31,7 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -46,6 +46,10 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
@@ -75,18 +79,16 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
private static final String TAG = "BaseSwipeUpHandler";
protected static final Rect TEMP_RECT = new Rect();
// Start resisting when swiping past this factor of mTransitionDragLength.
private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = ENABLE_OVERVIEW_ACTIONS.get()
? 2.8f : 1.4f;
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = ENABLE_OVERVIEW_ACTIONS.get()
? 3.6f : 1.8f;
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
// The distance needed to drag to reach the task size in recents.
protected int mTransitionDragLength;
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
// Start resisting when swiping past this factor of mTransitionDragLength.
private float mDragLengthFactorStartPullback = 1f;
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
private float mDragLengthFactorMaxPullback = 1f;
protected final Context mContext;
protected final RecentsAnimationDeviceState mDeviceState;
@@ -94,7 +96,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
protected final BaseActivityInterface<T> mActivityInterface;
protected final InputConsumerController mInputConsumer;
protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
protected final TransformParams mTransformParams = new TransformParams();
// Shift in the range of [0, 1].
@@ -123,6 +125,8 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
private PagedViewOrientedState mOrientedState;
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer) {
mContext = context;
@@ -132,20 +136,18 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
mActivityInitListener =
mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumer = inputConsumer;
mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
.getDeviceProfile(mContext));
}
protected void performHapticFeedback() {
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null;
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode navBarRotationMode) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null;
}
@UiThread
@@ -158,12 +160,12 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
} else {
float translation = Math.max(displacement, 0);
shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
if (shift > mDragLengthFactorStartPullback) {
float pullbackProgress = Utilities.getProgress(shift,
DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
mDragLengthFactorStartPullback, mDragLengthFactor);
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
* (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
shift = mDragLengthFactorStartPullback + pullbackProgress
* (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback);
}
}
@@ -326,10 +328,25 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
// we otherwise use the minimized home bounds provided by the system.
mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
}
int displayRotation = 0;
if (mOrientedState != null) {
// TODO(b/150300347): The first recents animation after launcher is started with the
// foreground app not in landscape will look funky until that bug is fixed
displayRotation = mOrientedState.getDisplayRotation();
}
RotationHelper.getTargetRectForRotation(TEMP_RECT, dp.widthPx, dp.heightPx,
displayRotation);
mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
// TODO(b/149609070): Landscape apps are currently limited in
// their ability to scale past the target rect.
float dragFactor = (float) dp.heightPx / mTransitionDragLength;
mDragLengthFactor = displayRotation == 0 ? dragFactor : Math.min(1.0f, dragFactor);
Pair<Float, Float> dragFactorStartAndMaxProgress =
mActivityInterface.getSwipeUpPullbackStartAndMaxProgress();
mDragLengthFactorStartPullback = dragFactorStartAndMaxProgress.first;
mDragLengthFactorMaxPullback = dragFactorStartAndMaxProgress.second;
}
}
@@ -338,7 +355,17 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
*/
protected abstract boolean moveWindowWithRecentsScroll();
protected abstract boolean onActivityInit(Boolean alreadyOnHome);
protected boolean onActivityInit(Boolean alreadyOnHome) {
T createdActivity = mActivityInterface.getCreatedActivity();
if (createdActivity != null) {
mOrientedState = ((RecentsView) createdActivity.getOverviewPanel())
.getPagedViewOrientedState();
mAppWindowAnimationHelper = new AppWindowAnimationHelper(mOrientedState, mContext);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
return true;
}
/**
* Called to create a input proxy for the running task
@@ -382,23 +409,32 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
*/
protected void applyTransformUnchecked() {
float shift = mCurrentShift.value;
float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
mAppWindowAnimationHelper.getTargetRect().width());
float offset = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
float taskSize = getOrientationHandler()
.getPrimarySize(mAppWindowAnimationHelper.getTargetRect());
float offsetScale = getTaskCurveScaleForOffset(offset, taskSize);
mTransformParams.setProgress(shift)
.setOffsetX(offsetX)
.setOffset(offset)
.setOffsetScale(offsetScale)
.setTargetSet(mRecentsAnimationTargets)
.setLauncherOnTop(true);
mAppWindowAnimationHelper.applyTransform(mTransformParams);
}
private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 + mPageSpacing;
float interpolation = Math.min(1, offsetX / distanceToReachEdge);
private float getTaskCurveScaleForOffset(float offset, float taskSize) {
int dpPixel = getOrientationHandler().getShortEdgeLength(mDp);
float distanceToReachEdge = dpPixel / 2 + taskSize / 2 + mPageSpacing;
float interpolation = Math.min(1, offset / distanceToReachEdge);
return TaskView.getCurveScaleForInterpolation(interpolation);
}
protected PagedOrientationHandler getOrientationHandler() {
if (mOrientedState == null) {
return new PortraitPagedViewHandler();
}
return mOrientedState.getOrientationHandler();
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
@@ -406,16 +442,19 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
*/
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RectF startRect = new RectF(
mAppWindowAnimationHelper.applyTransform(
mTransformParams.setProgress(startProgress)
.setTargetSet(mRecentsAnimationTargets)
.setLauncherOnTop(false)));
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
final View floatingView = homeAnimationFactory.getFloatingView();
final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources());
final RectF startRect = new RectF(
mAppWindowAnimationHelper.applyTransform(
mTransformParams.setProgress(startProgress)
.setTargetSet(mRecentsAnimationTargets)
.setLauncherOnTop(false)));
if (isFloatingIconView) {
RotationHelper.mapInverseRectFromNormalOrientation(startRect,
mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
}
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
if (isFloatingIconView) {
FloatingIconView fiv = (FloatingIconView) floatingView;
anim.addAnimatorListener(fiv);
@@ -435,6 +474,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
// 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 ? 1f - SHAPE_PROGRESS_DURATION : 1f;
final RectF rotatedRect = new RectF();
anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
@Override
@@ -445,9 +485,12 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
.setCurrentRect(currentRect)
.setTargetAlpha(getWindowAlpha(progress));
rotatedRect.set(currentRect);
if (isFloatingIconView) {
RotationHelper.mapRectFromNormalOrientation(rotatedRect,
mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
* (1f - progress));
}
mAppWindowAnimationHelper.applyTransform(mTransformParams);
@@ -30,10 +30,11 @@ import android.graphics.RectF;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.ActivityInitListener;
@@ -161,12 +161,12 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
@Override
protected boolean onActivityInit(Boolean alreadyOnHome) {
super.onActivityInit(alreadyOnHome);
mActivity = mActivityInterface.getCreatedActivity();
mRecentsView = mActivity.getOverviewPanel();
linkRecentsViewScroll();
mRecentsView.setDisallowScrollToClearAll(true);
mRecentsView.getClearAllButton().setVisibilityAlpha(0);
mRecentsView.setZoomProgress(1);
if (!mContinuingLastGesture) {
@@ -177,6 +177,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
}
}
mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
mDeviceState.enableMultipleRegions(false);
return true;
}
@@ -419,7 +420,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
this::createNewInputProxyHandler);
RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
anim.addAnimatorListener(endListener);
anim.start(mEndVelocityPxPerMs);
anim.start(mContext, mEndVelocityPxPerMs);
mFinishAnimation = RunningWindowAnim.wrap(anim);
} else {
@@ -469,7 +470,8 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
HomeAnimationFactory factory = new HomeAnimationFactory() {
@Override
public RectF getWindowTargetRect() {
return HomeAnimationFactory.getDefaultWindowTargetRect(mDp);
return HomeAnimationFactory
.getDefaultWindowTargetRect(mRecentsView.getPagedOrientationHandler(), mDp);
}
@Override
@@ -38,6 +38,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.UserHandle;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -54,6 +55,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -78,6 +80,8 @@ import java.util.function.Predicate;
public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
private Runnable mAdjustInterpolatorsRunnable;
private Pair<Float, Float> mSwipeUpPullbackStartAndMaxProgress =
BaseActivityInterface.super.getSwipeUpPullbackStartAndMaxProgress();
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
@@ -91,6 +95,11 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
}
}
@Override
public Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
return mSwipeUpPullbackStartAndMaxProgress;
}
@Override
public void onTransitionCancelled(boolean activityVisible) {
Launcher launcher = getCreatedActivity();
@@ -176,7 +185,8 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
if (canUseWorkspaceView) {
return iconLocation;
} else {
return HomeAnimationFactory.getDefaultWindowTargetRect(dp);
return HomeAnimationFactory
.getDefaultWindowTargetRect(recentsView.getPagedOrientationHandler(), dp);
}
}
@@ -268,23 +278,28 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
float scrollOffsetX = recentsView.getScrollOffset();
float offscreenX = recentsView.getOffscreenTranslationX(currScale);
float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
float fromTranslation = attached ? offscreenX - scrollOffsetX : 0;
float toTranslation = attached ? 0 : offscreenX - scrollOffsetX;
launcher.getStateManager()
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
PagedOrientationHandler pagedOrientationHandler =
recentsView.getPagedViewOrientedState().getOrientationHandler();
if (!recentsView.isShown() && animate) {
recentsView.setTranslationX(fromTranslationX);
pagedOrientationHandler
.getPrimaryViewTranslate().set(recentsView, fromTranslation);
} else {
fromTranslationX = recentsView.getTranslationX();
fromTranslation =
pagedOrientationHandler.getPrimaryViewTranslate().get(recentsView);
}
if (!animate) {
recentsView.setTranslationX(toTranslationX);
pagedOrientationHandler
.getPrimaryViewTranslate().set(recentsView, toTranslation);
} else {
launcher.getStateManager().createStateElementAnimation(
INDEX_RECENTS_TRANSLATE_X_ANIM,
fromTranslationX, toTranslationX).start();
fromTranslation, toTranslation).start();
}
fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
@@ -377,6 +392,14 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
return newT <= 1f ? newT : newT + normalizedTranslationY * (newT - 1);
});
};
// Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
float pullbackStartProgress = (0.75f - fromScaleAndTranslation.scale)
/ (endScaleAndTranslation.scale - fromScaleAndTranslation.scale);
float pullbackMaxProgress = (0.5f - fromScaleAndTranslation.scale)
/ (endScaleAndTranslation.scale - fromScaleAndTranslation.scale);
mSwipeUpPullbackStartAndMaxProgress = new Pair<>(
pullbackStartProgress, pullbackMaxProgress);
}
@Override
@@ -261,6 +261,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
@Override
protected boolean onActivityInit(Boolean alreadyOnHome) {
super.onActivityInit(alreadyOnHome);
final T activity = mActivityInterface.getCreatedActivity();
if (mActivity == activity) {
return true;
@@ -770,6 +771,16 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
}
if (endTarget == NEW_TASK) {
SystemUiProxy.INSTANCE.get(mContext).onQuickSwitchToNewTask();
}
if (endTarget == RECENTS || endTarget == HOME) {
// Since we're now done quickStepping, we want to only listen for touch events
// for the main orientation's nav bar, instead of multiple
mDeviceState.enableMultipleRegions(false);
}
if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
return LAST_TASK;
}
@@ -929,7 +940,8 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
windowAnim.start(velocityPxPerMs);
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
windowAnim.start(mContext, velocityPxPerMs);
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
mLauncherTransitionController = null;
@@ -183,7 +183,8 @@ public final class RecentsActivity extends BaseRecentsActivity {
RemoteAnimationTargetCompat[] wallpaperTargets) {
AnimatorSet target = new AnimatorSet();
boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
AppWindowAnimationHelper helper = new AppWindowAnimationHelper(this);
AppWindowAnimationHelper helper = new AppWindowAnimationHelper(
mFallbackRecentsView.getPagedViewOrientedState(), this);
target.play(getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
@@ -31,6 +31,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -67,6 +68,13 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
/**
* @return a launcher-provided OverscrollPlugin if available, otherwise null
*/
public OverscrollPlugin getLocalOverscrollPlugin() {
return null;
}
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
}
@@ -87,5 +95,12 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
* Called when the overlay is no longer used.
*/
public void reset() { }
/**
* Whether the overlay is modal, which means only tapping is enabled, but no swiping.
*/
public boolean isOverlayModal() {
return false;
}
}
}
@@ -17,13 +17,7 @@ package com.android.quickstep;
import static android.view.MotionEvent.ACTION_DOWN;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
@@ -56,6 +50,8 @@ import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.config.FeatureFlags;
@@ -80,6 +76,7 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -449,6 +446,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
mDeviceState.setOrientationTransformIfNeeded(event);
if (event.getAction() == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -502,17 +501,32 @@ public class TouchInteractionService extends Service implements PluginListener<O
|| previousGestureState.isRecentsAnimationRunning()
? newBaseConsumer(previousGestureState, newGestureState, event)
: mResetGestureInputConsumer;
// TODO(b/149880412): 2 button landscape mode is wrecked. Fixit!
if (mDeviceState.isFullyGesturalNavMode()) {
handleOrientationSetup(base);
if (mDeviceState.canTriggerAssistantAction(event)) {
base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
}
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()
&& (mOverscrollPlugin != null)
&& mOverscrollPlugin.isActive()) {
// Put the overscroll gesture as higher priority than the Assistant or base gestures
base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
mOverscrollPlugin);
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
OverscrollPlugin plugin = null;
if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
TaskOverlayFactory factory =
TaskOverlayFactory.INSTANCE.get(getApplicationContext());
plugin = factory.getLocalOverscrollPlugin(); // may be null
}
// If not local plugin was forced, use the actual overscroll plugin if available.
if (plugin == null && mOverscrollPlugin != null && mOverscrollPlugin.isActive()) {
plugin = mOverscrollPlugin;
}
if (plugin != null) {
// Put the overscroll gesture as higher priority than the Assistant or base
// gestures
base = new OverscrollInputConsumer(this, newGestureState, base,
mInputMonitorCompat, plugin);
}
}
if (mDeviceState.isScreenPinningActive()) {
@@ -533,6 +547,22 @@ public class TouchInteractionService extends Service implements PluginListener<O
return base;
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
if (!PagedView.sFlagForcedRotation) {
return;
}
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
Launcher l = (Launcher) mOverviewComponentObserver
.getActivityInterface().getCreatedActivity();
if (l == null || !(l.getOverviewPanel() instanceof RecentsView)) {
return;
}
((RecentsView)l.getOverviewPanel())
.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
mDeviceState.getDisplayRotation());
l.getDragLayer().recreateControllers();
}
private InputConsumer newBaseConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event) {
if (mDeviceState.isKeyguardShowingOccluded()) {
@@ -732,10 +762,18 @@ public class TouchInteractionService extends Service implements PluginListener<O
FeatureFlags.dump(pw);
PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
mDeviceState.dump(pw);
mOverviewComponentObserver.dump(pw);
if (mOverviewComponentObserver != null) {
mOverviewComponentObserver.dump(pw);
}
if (mGestureState != null) {
mGestureState.dump(pw);
}
pw.println("TouchState:");
BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
: mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" createdOverviewActivity=" + createdOverviewActivity);
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
@@ -195,4 +195,9 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity> {
}
super.applyLoadPlan(tasks);
}
@Override
protected boolean supportsVerticalLandscape() {
return false;
}
}
@@ -176,8 +176,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
// Proxy events to recents view
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
mNavBarPosition.getRotationMode()));
mRecentsViewDispatcher.setConsumer(mInteractionHandler
.getRecentsViewDispatcher(mNavBarPosition.getRotationMode()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -364,7 +364,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
: mNavBarPosition.isLeftEdge()
? -velocityX
: velocityY;
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
mDownPos);
@@ -15,12 +15,6 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Matrix;
@@ -28,6 +22,7 @@ import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.view.Surface;
import androidx.annotation.Nullable;
@@ -37,6 +32,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
@@ -50,6 +46,12 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
/**
* Utility class to handle window clip animation
*/
@@ -82,6 +84,7 @@ public class AppWindowAnimationHelper {
private final Rect mTmpRect = new Rect();
private final RectF mTmpRectF = new RectF();
private final RectF mCurrentRectWithInsets = new RectF();
private PagedViewOrientedState mOrientedState;
// Corner radius of windows, in pixels
private final float mWindowCornerRadius;
// Corner radius of windows when they're in overview mode.
@@ -100,20 +103,24 @@ public class AppWindowAnimationHelper {
private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
public AppWindowAnimationHelper(Context context) {
public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) {
mOrientedState = orientedState;
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
mTaskCornerRadius = TaskCornerRadius.get(context);
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
}
public AppWindowAnimationHelper(Context context) {
this(null, context);
}
private void updateSourceStack(RemoteAnimationTargetCompat target) {
mSourceInsets.set(target.contentInsets);
mSourceStackBounds.set(target.sourceContainerBounds);
// TODO: Should sourceContainerBounds already have this offset?
mSourceStackBounds.offsetTo(target.position.x, target.position.y);
}
public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
@@ -138,8 +145,9 @@ public class AppWindowAnimationHelper {
// from the source rect. The difference between the target rect (scaled to the
// source rect) is the amount to clip on each edge.
RectF scaledTargetRect = new RectF(mTargetRect);
Utilities.scaleRectFAboutCenter(scaledTargetRect,
mSourceRect.width() / mTargetRect.width());
float scale = getSrcToTargetScale();
Utilities.scaleRectFAboutCenter(scaledTargetRect, scale);
scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
mSourceWindowClipInsets.set(
Math.max(scaledTargetRect.left, 0),
@@ -149,6 +157,16 @@ public class AppWindowAnimationHelper {
mSourceRect.set(scaledTargetRect);
}
private float getSrcToTargetScale() {
if (mOrientedState == null ||
(mOrientedState.getDisplayRotation() == Surface.ROTATION_0
|| mOrientedState.getDisplayRotation() == Surface.ROTATION_180)) {
return mSourceRect.width() / mTargetRect.width();
} else {
return mSourceRect.height() / mTargetRect.height();
}
}
public void prepareAnimation(DeviceProfile dp, boolean isOpening) {
mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode;
@@ -221,7 +239,6 @@ public class AppWindowAnimationHelper {
layer = Integer.MAX_VALUE;
}
}
// Since radius is in Surface space, but we draw the rounded corners in screen space, we
// have to undo the scale.
surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
@@ -237,11 +254,16 @@ public class AppWindowAnimationHelper {
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale);
mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF));
mCurrentRect.offset(params.mOffsetX, 0);
if (mOrientedState == null || mOrientedState.areMultipleLayoutOrientationsDisabled()) {
mCurrentRect.offset(params.mOffset, 0);
} else {
int displayRotation = mOrientedState.getDisplayRotation();
mOrientedState.getOrientationHandler().offsetTaskRect(mCurrentRect,
params.mOffset, displayRotation);
}
}
updateClipRect(params);
return mCurrentRect;
}
@@ -340,7 +362,7 @@ public class AppWindowAnimationHelper {
* @return The source rect's scale and translation relative to the target rect.
*/
public LauncherState.ScaleAndTranslation getScaleAndTranslation() {
float scale = mSourceRect.width() / mTargetRect.width();
float scale = getSrcToTargetScale();
float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY();
return new LauncherState.ScaleAndTranslation(scale, 0, translationY);
}
@@ -390,7 +412,7 @@ public class AppWindowAnimationHelper {
public static class TransformParams {
private float mProgress;
private float mOffsetX;
private float mOffset;
private float mOffsetScale;
private @Nullable RectF mCurrentRect;
private float mTargetAlpha;
@@ -401,7 +423,7 @@ public class AppWindowAnimationHelper {
public TransformParams() {
mProgress = 0;
mOffsetX = 0;
mOffset = 0;
mOffsetScale = 1;
mCurrentRect = null;
mTargetAlpha = 1;
@@ -453,8 +475,8 @@ public class AppWindowAnimationHelper {
* the default), then offset the current rect by this amount after computing the rect based
* on {@link #mProgress}.
*/
public TransformParams setOffsetX(float offsetX) {
mOffsetX = offsetX;
public TransformParams setOffset(float offset) {
mOffset = offset;
return this;
}
@@ -504,8 +526,8 @@ public class AppWindowAnimationHelper {
return mProgress;
}
public float getOffsetX() {
return mOffsetX;
public float getOffset() {
return mOffset;
}
public float getOffsetScale() {
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
import android.animation.Animator;
import android.content.res.Resources;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
@@ -28,6 +28,8 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.FlingSpringAnim;
import com.android.launcher3.util.DynamicResource;
import com.android.systemui.plugins.ResourceProvider;
import java.util.ArrayList;
import java.util.List;
@@ -103,7 +105,7 @@ public class RectFSpringAnim {
private float mMinVisChange;
private float mYOvershoot;
public RectFSpringAnim(RectF startRect, RectF targetRect, Resources resources) {
public RectFSpringAnim(RectF startRect, RectF targetRect, Context context) {
mStartRect = startRect;
mTargetRect = targetRect;
mCurrentCenterX = mStartRect.centerX();
@@ -111,8 +113,9 @@ public class RectFSpringAnim {
mTrackingBottomY = startRect.bottom < targetRect.bottom;
mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top;
mMinVisChange = resources.getDimensionPixelSize(R.dimen.swipe_up_fling_min_visible_change);
mYOvershoot = resources.getDimensionPixelSize(R.dimen.swipe_up_y_overshoot);
ResourceProvider rp = DynamicResource.provider(context);
mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change);
mYOvershoot = rp.getDimension(R.dimen.swipe_up_y_overshoot);
}
public void onTargetPositionChanged() {
@@ -137,7 +140,12 @@ public class RectFSpringAnim {
mAnimatorListeners.add(animatorListener);
}
public void start(PointF velocityPxPerMs) {
/**
* Starts the fling/spring animation.
* @param context The activity context.
* @param velocityPxPerMs Velocity of swipe in px/ms.
*/
public void start(Context context, PointF velocityPxPerMs) {
// Only tell caller that we ended if both x and y animations have ended.
OnAnimationEndListener onXEndListener = ((animation, canceled, centerX, velocityX) -> {
mRectXAnimEnded = true;
@@ -152,7 +160,7 @@ public class RectFSpringAnim {
float endX = mTargetRect.centerX();
float minXValue = Math.min(startX, endX);
float maxXValue = Math.max(startX, endX);
mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, startX, endX,
mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
velocityPxPerMs.x * 1000, mMinVisChange, minXValue, maxXValue, 1f, onXEndListener);
float startVelocityY = velocityPxPerMs.y * 1000;
@@ -162,14 +170,18 @@ public class RectFSpringAnim {
float endY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top;
float minYValue = Math.min(startY, endY - mYOvershoot);
float maxYValue = Math.max(startY, endY);
mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY,
mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, startVelocityY,
mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
float minVisibleChange = Math.abs(1f / mStartRect.height());
ResourceProvider rp = DynamicResource.provider(context);
float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
float stiffness = rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
.setSpring(new SpringForce(1f)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
.setStiffness(SpringForce.STIFFNESS_LOW))
.setDampingRatio(damping)
.setStiffness(stiffness))
.setStartVelocity(velocityPxPerMs.y * minVisibleChange)
.setMaxValue(1f)
.setMinimumVisibleChange(minVisibleChange)
@@ -41,7 +41,9 @@ import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider;
/**
* Creates an animation where all the workspace items are moved into their final location,
@@ -55,9 +57,6 @@ public class StaggeredWorkspaceAnim {
private static final float MAX_VELOCITY_PX_PER_S = 22f;
private static final float DAMPING_RATIO = 0.7f;
private static final float STIFFNESS = 150f;
private final float mVelocity;
private final float mSpringTransY;
@@ -177,9 +176,12 @@ public class StaggeredWorkspaceAnim {
v.setTranslationY(mSpringTransY);
ResourceProvider rp = DynamicResource.provider(v.getContext());
float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
ObjectAnimator springTransY = new SpringAnimationBuilder<>(v, VIEW_TRANSLATE_Y)
.setStiffness(STIFFNESS)
.setDampingRatio(DAMPING_RATIO)
.setStiffness(stiffness)
.setDampingRatio(damping)
.setMinimumVisibleChange(1f)
.setEndValue(0)
.setStartVelocity(mVelocity)
@@ -21,7 +21,7 @@ import android.util.AttributeSet;
import android.util.Property;
import android.widget.Button;
import com.android.launcher3.Utilities;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.views.RecentsView.PageCallbacks;
import com.android.quickstep.views.RecentsView.ScrollState;
@@ -44,21 +44,26 @@ public class ClearAllButton extends Button implements PageCallbacks {
private float mContentAlpha = 1;
private float mVisibilityAlpha = 1;
private final boolean mIsRtl;
private boolean mIsRtl;
private int mScrollOffset;
private RecentsView mParent;
public ClearAllButton(Context context, AttributeSet attrs) {
super(context, attrs);
mIsRtl = Utilities.isRtl(context.getResources());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mScrollOffset = mIsRtl ? mParent.getPaddingRight() / 2 : - mParent.getPaddingLeft() / 2;
}
RecentsView parent = (RecentsView) getParent();
mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2;
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent = (RecentsView) getParent();
mIsRtl = !mParent.getPagedOrientationHandler().getRecentsRtlSetting(getResources());
}
@Override
@@ -73,6 +78,21 @@ public class ClearAllButton extends Button implements PageCallbacks {
}
}
public void onLayoutChanged() {
if (mParent == null) {
return;
}
setRotation(mParent.getPagedOrientationHandler().getDegreesRotated());
}
public void setRtl(boolean rtl) {
if (mIsRtl == rtl) {
return;
}
mIsRtl = rtl;
invalidate();
}
public void setVisibilityAlpha(float alpha) {
if (mVisibilityAlpha != alpha) {
mVisibilityAlpha = alpha;
@@ -82,14 +102,16 @@ public class ClearAllButton extends Button implements PageCallbacks {
@Override
public void onPageScroll(ScrollState scrollState) {
float width = getWidth();
if (width == 0) {
PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler();
float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
if (orientationSize == 0) {
return;
}
float shift = Math.min(scrollState.scrollFromEdge, width);
setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift));
mScrollAlpha = 1 - shift / width;
float shift = Math.min(scrollState.scrollFromEdge, orientationSize);
float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift);
orientationHandler.setPrimaryAndResetSecondaryTranslate(this, translation);
mScrollAlpha = 1 - shift / orientationSize;
updateAlpha();
}
@@ -42,10 +42,12 @@ import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
@@ -85,6 +87,10 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
}
};
private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener =
isForcedRotation -> LauncherRecentsView.this
.disableMultipleLayoutRotations(!isForcedRotation);
public LauncherRecentsView(Context context) {
this(context, null);
}
@@ -180,19 +186,21 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
* @return The translationX to apply to this view so that the first task is just offscreen.
*/
public float getOffscreenTranslationX(float recentsScale) {
float offscreenX = NORMAL.getOverviewScaleAndTranslation(mActivity).translationX;
LauncherState.ScaleAndTranslation overviewScaleAndTranslation =
NORMAL.getOverviewScaleAndTranslation(mActivity);
float offscreen = mOrientationHandler.getTranslationValue(overviewScaleAndTranslation);
// Offset since scale pushes tasks outwards.
getTaskSize(sTempRect);
int taskWidth = sTempRect.width();
offscreenX += taskWidth * (recentsScale - 1) / 2;
int taskSize = mOrientationHandler.getPrimarySize(sTempRect);
offscreen += taskSize * (recentsScale - 1) / 2;
if (mRunningTaskTileHidden) {
// The first task is hidden, so offset by its width.
offscreenX -= (taskWidth + getPageSpacing()) * recentsScale;
offscreen -= (taskSize + getPageSpacing()) * recentsScale;
}
if (isRtl()) {
offscreenX = -offscreenX;
offscreen = -offscreen;
}
return offscreenX;
return offscreen;
}
@Override
@@ -276,6 +284,11 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
return mTransformParams;
}
@Override
protected boolean supportsVerticalLandscape() {
return PagedView.sFlagForcedRotation;
}
@Override
public void reset() {
super.reset();
@@ -329,6 +342,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
super.onAttachedToWindow();
PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(
mRecentsExtraCardPluginListener, RecentsExtraCard.class);
mActivity.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener);
}
@Override
@@ -336,22 +350,23 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
super.onDetachedFromWindow();
PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(
mRecentsExtraCardPluginListener);
mActivity.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener);
}
@Override
protected int computeMinScrollX() {
protected int computeMinScroll() {
if (canComputeScrollX() && !mIsRtl) {
return computeScrollX();
}
return super.computeMinScrollX();
return super.computeMinScroll();
}
@Override
protected int computeMaxScrollX() {
protected int computeMaxScroll() {
if (canComputeScrollX() && mIsRtl) {
return computeScrollX();
}
return super.computeMaxScrollX();
return super.computeMaxScroll();
}
private boolean canComputeScrollX() {
@@ -22,7 +22,6 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
@@ -30,6 +29,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
@@ -52,7 +52,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -66,11 +65,14 @@ import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.util.SparseBooleanArray;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -80,26 +82,32 @@ import android.view.animation.Interpolator;
import android.widget.ListView;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
@@ -108,12 +116,14 @@ import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.LayoutUtils;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -162,6 +172,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
};
private final OrientationEventListener mOrientationListener;
private int mPreviousRotation;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
@@ -198,7 +210,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private boolean mDwbToastShown;
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
private boolean mFreezeViewVisibility;
protected boolean mFreezeViewVisibility;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -268,15 +280,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
};
private final IPinnedStackAnimationListener mIPinnedStackAnimationListener =
new IPinnedStackAnimationListener.Stub() {
@Override
public void onPinnedStackAnimationStarted() {
// Needed for activities that auto-enter PiP, which will not trigger a remote
// animation to be created
mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
}
};
private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
new PinnedStackAnimationListener();
// Used to keep track of the last requested task list id, so that we do not request to load the
// tasks again if we have already requested it and the task list has not changed
@@ -321,6 +326,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
// Keeps track of the index where the first TaskView should be
private int mTaskViewStartIndex = 0;
private View mActionsView;
private boolean mGestureRunning = false;
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
@@ -340,7 +347,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mActivity = (T) BaseActivity.fromContext(context);
mModel = RecentsModel.INSTANCE.get(context);
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
mTempAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mTempAppWindowAnimationHelper =
new AppWindowAnimationHelper(getPagedViewOrientedState(), context);
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
@@ -348,7 +356,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
mIsRtl = !Utilities.isRtl(getResources());
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
@@ -368,9 +376,26 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
.getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
setWillNotDraw(false);
updateEmptyMessage();
disableMultipleLayoutRotations(!supportsVerticalLandscape());
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
mOrientationListener = new OrientationEventListener(getContext()) {
@Override
public void onOrientationChanged(int i) {
int rotation = RotationHelper.getRotationFromDegrees(i);
if (mPreviousRotation != rotation) {
animateRecentsRotationInPlace(rotation);
if (rotation == 0) {
showActionsView();
} else {
hideActionsView();
}
mPreviousRotation = rotation;
}
}
};
}
public OverScroller getScroller() {
@@ -451,8 +476,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIdp.addOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(mActivity);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
mIPinnedStackAnimationListener);
addActionsView();
}
@Override
@@ -466,6 +493,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
mIPinnedStackAnimationListener.setActivity(null);
}
@Override
@@ -496,6 +524,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
public void setOverviewStateEnabled(boolean enabled) {
if (supportsVerticalLandscape() && mOrientationListener.canDetectOrientation()) {
if (enabled) {
mOrientationListener.enable();
} else {
mOrientationListener.disable();
}
}
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
if (!enabled) {
@@ -573,6 +608,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev);
}
@Override
protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
// Enables swiping to the left or right only if the task overlay is not modal.
if (getCurrentPageTaskView() == null || !getCurrentPageTaskView().isTaskOverlayModal()) {
super.determineScrollingStart(ev, touchSlopScale);
}
}
protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
return true;
}
@@ -599,6 +641,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (getTaskViewCount() != requiredTaskCount) {
if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
hideActionsView();
}
for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
addView(mTaskViewPool.getView());
@@ -608,6 +651,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
if (requiredTaskCount > 0) {
addView(mClearAllButton);
showActionsView();
}
}
@@ -616,7 +660,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
final Task task = tasks.get(i);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task);
taskView.bind(task, mLayoutRotation);
}
if (mNextPage == INVALID_PAGE) {
@@ -647,6 +691,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
}
hideActionsView();
}
public int getTaskViewCount() {
@@ -748,19 +793,21 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
return;
}
int scrollX = getScrollX();
final int halfPageWidth = getNormalChildWidth() / 2;
final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth;
final int halfScreenWidth = getMeasuredWidth() / 2;
CurveProperties curveProperties = mOrientationHandler
.getCurveProperties(this, mInsets);
int scroll = curveProperties.scroll;
final int halfPageSize = curveProperties.halfPageSize;
final int screenCenter = curveProperties.screenCenter;
final int halfScreenSize = curveProperties.halfScreenSize;
final int pageSpacing = mPageSpacing;
mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX);
mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll);
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
View page = getPageAt(i);
float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth;
float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing;
mScrollState.linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
((PageCallbacks) page).onPageScroll(mScrollState);
@@ -892,6 +939,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
setRunningTaskIconScaledDown(true);
mGestureRunning = true;
}
/**
@@ -908,6 +956,47 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
setSwipeDownShouldLaunchApp(true);
}
private void animateRecentsRotationInPlace(int newRotation) {
if (!supportsVerticalLandscape()) {
return;
}
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
updateLayoutRotation(newRotation);
((DragLayer)mActivity.getDragLayer()).recreateControllers();
rotateAllChildTasks();
setRecentsChangedOrientation(false).start();
}
});
pa.start();
}
public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
getRunningTaskIndex();
int runningIndex = getCurrentPage();
AnimatorSet as = new AnimatorSet();
for (int i = 0; i < getTaskViewCount(); i++) {
if (runningIndex == i) {
continue;
}
View taskView = getTaskViewAt(i);
as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
}
return as;
}
abstract protected boolean supportsVerticalLandscape();
private void rotateAllChildTasks() {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = getTaskViewAt(i);
taskView.setOverviewRotation(mLayoutRotation);
}
}
/**
* Called when a gesture from an app has finished.
*/
@@ -920,6 +1009,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
setRunningTaskHidden(false);
animateUpRunningTaskIconScale();
mGestureRunning = false;
}
/**
@@ -936,6 +1026,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
showActionsView();
}
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
@@ -943,7 +1034,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
false, true, false, false, new ActivityManager.TaskDescription(), 0,
new ComponentName("", ""), false);
taskView.bind(mTmpRunningTask);
taskView.bind(mTmpRunningTask, mLayoutRotation);
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -1119,15 +1210,22 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
FloatProperty<View> secondaryViewTranslate =
mOrientationHandler.getSecondaryViewTranslate();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor();
if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) {
addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y,
MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
SpringForce.STIFFNESS_MEDIUM,
0, -taskView.getHeight()),
ResourceProvider rp = DynamicResource.provider(mActivity);
float dampingRatio = rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio);
float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness);
addAnim(new SpringObjectAnimator<>(taskView, secondaryViewTranslate,
MIN_VISIBLE_CHANGE_PIXELS, dampingRatio,
stiffness, 0, verticalFactor * secondaryTaskDimension),
duration, LINEAR, anim);
} else {
addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
duration, LINEAR, anim);
addAnim(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
verticalFactor * secondaryTaskDimension), duration, LINEAR, anim);
}
}
@@ -1158,11 +1256,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
int[] oldScroll = new int[count];
getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
int[] newScroll = new int[count];
getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView);
int taskCount = getTaskViewCount();
int scrollDiffPerPage = 0;
if (count > 1) {
@@ -1200,13 +1296,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
if (UNSTABLE_SPRINGS.get() && child instanceof TaskView) {
ResourceProvider rp = DynamicResource.provider(mActivity);
float damping = rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio);
float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness);
addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X,
MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
SpringForce.STIFFNESS_MEDIUM,
0, scrollDiff), duration, ACCEL, anim);
MIN_VISIBLE_CHANGE_PIXELS, damping,
stiffness, 0, scrollDiff), duration, ACCEL, anim);
} else {
addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration,
ACCEL, anim);
Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
addAnim(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff),
duration, ACCEL, anim);
}
needsCurveUpdates = true;
@@ -1252,6 +1352,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (getTaskViewCount() == 0) {
removeView(mClearAllButton);
hideActionsView();
startHome();
} else {
snapToPageImmediately(pageToSnapTo);
@@ -1396,15 +1497,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
}
mClearAllButton.setContentAlpha(mContentAlpha);
int alphaInt = Math.round(alpha * 255);
mEmptyMessagePaint.setAlpha(alphaInt);
mEmptyIcon.setAlpha(alphaInt);
if (alpha > 0) {
setVisibility(VISIBLE);
if (!mGestureRunning) {
showActionsView();
}
} else if (!mFreezeViewVisibility) {
setVisibility(GONE);
hideActionsView();
}
}
@@ -1418,10 +1521,27 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (!mFreezeViewVisibility) {
setVisibility(mContentAlpha > 0 ? VISIBLE : GONE);
if (mContentAlpha > 0) {
showActionsView();
} else {
hideActionsView();
}
}
}
}
@Override
public void setLayoutRotation(int touchRotation, int displayRotation) {
if (!sFlagForcedRotation) {
return;
}
super.setLayoutRotation(touchRotation, displayRotation);
mClearAllButton.onLayoutChanged();
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
@@ -1642,7 +1762,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
});
AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(mActivity);
AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(
getPagedViewOrientedState(), mActivity);
appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
@@ -1799,7 +1920,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
@Override
protected int computeMinScrollX() {
protected int computeMinScroll() {
if (getTaskViewCount() > 0) {
if (mDisallowScrollToClearAll) {
// We aren't showing the clear all button,
@@ -1814,11 +1935,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
return getScrollForPage(mTaskViewStartIndex);
}
return super.computeMinScrollX();
return super.computeMinScroll();
}
@Override
protected int computeMaxScrollX() {
protected int computeMaxScroll() {
if (getTaskViewCount() > 0) {
if (mDisallowScrollToClearAll) {
// We aren't showing the clear all button,
@@ -1833,7 +1954,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
}
return super.computeMaxScrollX();
return super.computeMaxScroll();
}
public ClearAllButton getClearAllButton() {
@@ -1848,31 +1969,30 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return 0;
}
int startScroll = getScrollForPage(getRunningTaskIndex());
int offsetX = startScroll - getScrollX();
offsetX *= getScaleX();
int offsetX = startScroll - mOrientationHandler.getPrimaryScroll(this);
offsetX *= mOrientationHandler.getPrimaryScale(this);
return offsetX;
}
public Consumer<MotionEvent> getEventDispatcher(RotationMode rotationMode) {
if (rotationMode.isTransposed) {
Matrix transform = new Matrix();
transform.setRotate(-rotationMode.surfaceRotation);
if (getWidth() > 0 && getHeight() > 0) {
float scale = ((float) getWidth()) / getHeight();
transform.postScale(scale, 1 / scale);
}
Matrix inverse = new Matrix();
transform.invert(inverse);
return e -> {
e.transform(transform);
super.onTouchEvent(e);
e.transform(inverse);
};
public Consumer<MotionEvent> getEventDispatcher(RotationMode navBarRotationMode) {
float degreesRotated;
if (navBarRotationMode == RotationMode.NORMAL) {
degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation);
} else {
degreesRotated = -navBarRotationMode.surfaceRotation;
}
if (degreesRotated == 0) {
return super::onTouchEvent;
}
// At this point the event coordinates have already been transformed, so we need to
// undo that transformation since PagedView also accommodates for the transformation via
// PagedOrientationHandler
return e -> {
RotationHelper.transformEvent(-degreesRotated, e, true);
super.onTouchEvent(e);
RotationHelper.transformEvent(-degreesRotated, e, false);
};
}
public AppWindowAnimationHelper getClipAnimationHelper() {
@@ -1955,4 +2075,51 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
/** @param isEmpty Whether RecentsView is empty (i.e. has no children) */
void onEmptyMessageUpdated(boolean isEmpty);
}
private static class PinnedStackAnimationListener<T extends BaseActivity> extends
IPinnedStackAnimationListener.Stub {
private T mActivity;
public void setActivity(T activity) {
mActivity = activity;
}
@Override
public void onPinnedStackAnimationStarted() {
// Needed for activities that auto-enter PiP, which will not trigger a remote
// animation to be created
mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
}
}
private void showActionsView() {
if (mActionsView != null && getTaskViewCount() > 0) {
mActionsView.setVisibility(VISIBLE);
}
}
private void hideActionsView() {
if (mActionsView != null) {
mActionsView.setVisibility(GONE);
}
}
private void addActionsView() {
if (mActionsView == null && ENABLE_OVERVIEW_ACTIONS.get()
&& SysUINavigationMode.removeShelfFromOverview(mActivity)) {
mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view);
if (mActionsView != null) {
Rect rect = new Rect();
getTaskSize(rect);
InsettableFrameLayout.LayoutParams layoutParams =
new InsettableFrameLayout.LayoutParams(rect.width(),
getResources().getDimensionPixelSize(
R.dimen.overview_actions_height));
layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
mActionsView.setLayoutParams(layoutParams);
showActionsView();
}
}
}
}
@@ -36,6 +36,7 @@ import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.view.Surface;
import android.view.View;
@@ -16,6 +16,12 @@
package com.android.quickstep.views;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
@@ -23,7 +29,6 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,25 +46,24 @@ import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.view.Gravity;
import android.view.Surface;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -166,8 +170,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private final float mWindowCornerRadius;
private final BaseDraggingActivity mActivity;
@Nullable private View mActionsView;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
private float mFocusTransitionProgress = 1;
@@ -237,18 +239,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context);
mSnapshotView.setLayoutParams(thumbnailParams);
}
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
mActionsView = mSnapshotView.getTaskOverlay().getActionsView();
if (mActionsView != null) {
TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
getResources().getDimensionPixelSize(R.dimen.overview_actions_height),
Gravity.BOTTOM);
addView(mActionsView, params);
mActionsView.setAlpha(0);
}
}
public boolean isTaskOverlayModal() {
return mSnapshotView.getTaskOverlay().isOverlayModal();
}
public TaskMenuView getMenuView() {
@@ -262,10 +256,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
/**
* Updates this task view to the given {@param task}.
*/
public void bind(Task task) {
public void bind(Task task, int recentsRotation) {
cancelPendingLoadTasks();
mTask = task;
mSnapshotView.bind(task);
setOverviewRotation(recentsRotation);
}
public Task getTask() {
@@ -435,6 +430,42 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
void setOverviewRotation(int iconRotation) {
PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources());
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext());
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
int rotation = RotationHelper.getDegreesFromRotation(iconRotation);
switch (iconRotation) {
case Surface.ROTATION_90:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
iconParams.rightMargin = -thumbnailPadding;
iconParams.leftMargin = iconParams.topMargin = iconParams.bottomMargin = 0;
break;
case Surface.ROTATION_180:
iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
iconParams.bottomMargin = -thumbnailPadding;
iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
break;
case Surface.ROTATION_270:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
iconParams.leftMargin = -thumbnailPadding;
iconParams.rightMargin = iconParams.topMargin = iconParams.bottomMargin = 0;
break;
case Surface.ROTATION_0:
default:
iconParams.gravity = TOP | CENTER_HORIZONTAL;
iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin =
iconParams.bottomMargin = 0;
break;
}
mSnapshotView.setLayoutParams(snapshotParams);
mIconView.setLayoutParams(iconParams);
mIconView.setRotation(rotation);
}
private void setIconAndDimTransitionProgress(float progress, boolean invert) {
if (invert) {
progress = 1 - progress;
@@ -449,11 +480,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mIconView.setScaleX(scale);
mIconView.setScaleY(scale);
if (mActionsView != null && isRunningTask()) {
mActionsView.setAlpha(scale);
}
mFooterVerticalOffset = 1.0f - scale;
for (FooterWrapper footer : mFooters) {
if (footer != null) {
@@ -544,31 +570,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mMenuView.setScaleX(getScaleX());
mMenuView.setScaleY(getScaleY());
}
// This is not the proper implementation and will be replaced with a proper layout.
if (mActionsView != null) {
if (mFocusTransitionProgress == 1f) {
mActionsView.setAlpha(1 - curveInterpolation / MAX_PAGE_SCRIM_ALPHA);
}
maintainActionViewPosition(curveScaleForCurveInterpolation);
}
}
private void maintainActionViewPosition(float curveScaleForCurveInterpolation) {
float inverseCurveScaleFactor = curveScaleForCurveInterpolation == 0 ? 0 :
(1f / curveScaleForCurveInterpolation);
mActionsView.setScaleX(inverseCurveScaleFactor);
mActionsView.setScaleY(inverseCurveScaleFactor);
mActionsView.setTranslationX(inverseCurveScaleFactor * (-getX()
+ getRecentsView().getScrollX() + getRecentsView().scrollOffsetLeft()));
mActionsView.setTranslationY(
(1f - curveScaleForCurveInterpolation) * (mSnapshotView.getHeight()
+ mActionsView.getHeight()) / 2f
+ inverseCurveScaleFactor * (-getTranslationY()));
}
/**
* Sets the footer at the specific index and returns the previously set footer.
*/
@@ -597,8 +600,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
addView(view, indexToAdd);
LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
layoutParams.gravity =
Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
layoutParams.bottomMargin =
((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin;
view.setAlpha(mFooterAlpha);
@@ -851,9 +853,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mFullscreenProgress = progress;
boolean isFullscreen = mFullscreenProgress > 0;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
if (mActionsView != null) {
mActionsView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
}
setClipChildren(!isFullscreen);
setClipToPadding(!isFullscreen);
@@ -934,5 +933,4 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mScale = scale;
}
}
}
-1
View File
@@ -31,7 +31,6 @@
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
android:layout_gravity="top|center_horizontal"
android:focusable="false"
android:importantForAccessibility="no"/>
</com.android.quickstep.views.TaskView>
+19 -14
View File
@@ -66,27 +66,32 @@
<!-- Content description for a close button. [CHAR LIMIT=NONE] -->
<string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
<!-- Hotseat migration notification title -->
<string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
<string translatable="false" name="hotseat_edu_prompt_title">Get app suggestions based on your routines</string>
<!-- Hotseat migration notification content -->
<string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
<!-- Hotseat migration wizard title -->
<string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
<!-- Hotseat migration wizard message -->
<string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
<string translatable="false" name="hotseat_edu_prompt_content">Tap to set up</string>
<!-- Hotseat educational strings for users who don't qualify for migration -->
<string translatable="false" name="hotseat_edu_title_migrate">Suggested apps replace the bottom row of apps</string>
<string translatable="false" name="hotseat_edu_message_migrate">Your hotseat items will be moved up on the homescreen</string>
<string translatable="false" name="hotseat_edu_message_migrate_alt">Your hotseat items will be moved to the last page of your workspace</string>
<!-- Hotseat educational strings for users who don't qualify -->
<string translatable="false" name="hotseat_edu_title_no_migrate">Suggested apps will be found at the bottom row of your home screen</string>
<string translatable="false" name="hotseat_edu_message_no_migrate">Drag one or many apps off the bottom row of home screen to see app suggestions</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
<string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved up.</string>
<string translatable="false" name="hotseat_items_migrated_alt">Bottom row of apps moved to last page.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string translatable="false" name="hotseat_no_migration">Bottom row won\'t be replaced. Manually drag apps for predictions.</string>
<!-- Button text to opt in for fully predicted hotseat -->
<string translatable="false" name="hotseat_migrate_accept">Turn On</string>
<string translatable="false" name="hotseat_edu_accept">Got it</string>
<!-- Button text to dismiss opt in for fully predicted hotseat -->
<string translatable="false" name="hotseat_migrate_dismiss">No thanks</string>
<!-- Hotseat onboard notification title -->
<string translatable="false" name="hotseat_onboard_notification_title">Your hotseat just got smarter</string>
<!-- Hotseat onboard notification detail -->
<string translatable="false" name="hotseat_onboard_notification_detail">Tap here to set it up</string>
<string translatable="false" name="hotseat_edu_dismiss">No thanks</string>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
@@ -0,0 +1,194 @@
/*
* 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 com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.graphics.Point;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.Surface;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.util.DefaultDisplay;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class OrientationTouchTransformerTest {
private static final int SIZE_WIDTH = 1080;
private static final int SIZE_HEIGHT = 2280;
private static final float DENSITY_DISPLAY_METRICS = 3.0f;
private OrientationTouchTransformer mTouchTransformer;
Resources mResources;
private DefaultDisplay.Info mInfo;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mResources = mock(Resources.class);
when(mResources.getBoolean(anyInt())).thenReturn(true);
when(mResources.getDimension(anyInt())).thenReturn(10.0f);
DisplayMetrics mockDisplayMetrics = new DisplayMetrics();
mockDisplayMetrics.density = DENSITY_DISPLAY_METRICS;
when(mResources.getDisplayMetrics()).thenReturn(mockDisplayMetrics);
mInfo = createDisplayInfo(Surface.ROTATION_0);
mTouchTransformer = new OrientationTouchTransformer(mResources, NO_BUTTON, () -> 0);
}
@Test
public void disabledMultipeRegions_shouldOverrideFirstRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
DefaultDisplay.Info info2 = createDisplayInfo(Surface.ROTATION_90);
mTouchTransformer.createOrAddTouchRegion(info2);
float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
MotionEvent inOldRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inOldRegion);
assertFalse(mTouchTransformer.touchInValidSwipeRegions(inOldRegion.getX(), inOldRegion.getY()));
// Override region
mTouchTransformer.createOrAddTouchRegion(mInfo);
inOldRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inOldRegion);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inOldRegion.getX(), inOldRegion.getY()));
}
@Test
public void allowMultipeRegions_shouldOverrideFirstRegion() {
DefaultDisplay.Info info2 = createDisplayInfo(Surface.ROTATION_90);
mTouchTransformer.createOrAddTouchRegion(info2);
// We have to add 0 rotation second so that gets set as the current rotation, otherwise
// matrix transform will fail (tests only work in Portrait at the moment)
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer.createOrAddTouchRegion(mInfo);
float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
MotionEvent inNewRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inNewRegion);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inNewRegion.getX(), inNewRegion.getY()));
}
@Test
public void applyTransform_taskNotFrozen_notInRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
MotionEvent outOfRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, 100);
mTouchTransformer.transform(outOfRegion);
assertFalse(mTouchTransformer.touchInValidSwipeRegions(outOfRegion.getX(), outOfRegion.getY()));
}
@Test
public void applyTransform_taskFrozen_noRotate_outOfRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
MotionEvent outOfRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, 100);
mTouchTransformer.transform(outOfRegion);
assertFalse(mTouchTransformer.touchInValidSwipeRegions(outOfRegion.getX(), outOfRegion.getY()));
}
@Test
public void applyTransform_taskFrozen_noRotate_inRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inRegion);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY()));
}
@Test
public void applyTransform_taskNotFrozen_noRotate_inDefaultRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inRegion);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY()));
}
@Test
public void applyTransform_taskNotFrozen_90Rotate_inRegion() {
mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
float y = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y);
mTouchTransformer.transform(inRegion);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY()));
}
@Test
@Ignore("There's too much that goes into needing to mock a real motion event so the "
+ "transforms in native code get applied correctly. Once that happens then maybe we can"
+ " write slightly more complex unit tests")
public void applyTransform_taskNotFrozen_90Rotate_inTwoRegions() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
// Landscape point
float y1 = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
MotionEvent inRegion1_down = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, y1);
MotionEvent inRegion1_up = generateMotionEvent(MotionEvent.ACTION_UP, 10, y1);
// Portrait point in landscape orientation axis
MotionEvent inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 10);
mTouchTransformer.transform(inRegion1_down);
mTouchTransformer.transform(inRegion2);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion1_down.getX(), inRegion1_down.getY()));
// We only process one gesture region until we see a MotionEvent.ACTION_UP
assertFalse(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
mTouchTransformer.transform(inRegion1_up);
// Set the new region with this MotionEvent.ACTION_DOWN
inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 370);
mTouchTransformer.transform(inRegion2);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
}
private DefaultDisplay.Info createDisplayInfo(int rotation) {
Point p = new Point(SIZE_WIDTH, SIZE_HEIGHT);
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
p = new Point(SIZE_HEIGHT, SIZE_WIDTH);
}
return new DefaultDisplay.Info(0, rotation, 0, p, p, p, null);
}
private float generateTouchRegionHeight(int rotation) {
float height = SIZE_HEIGHT;
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
height = SIZE_WIDTH;
}
return height - ResourceUtils.DEFAULT_NAVBAR_VALUE * DENSITY_DISPLAY_METRICS;
}
private MotionEvent generateMotionEvent(int motionAction, float x, float y) {
return MotionEvent.obtain(0, 0, motionAction, x, y, 0);
}
}
@@ -36,11 +36,13 @@ import android.os.CancellationSignal;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.SystemActions;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.UiThreadHelper;
@@ -51,6 +53,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.stream.Stream;
@@ -61,6 +64,8 @@ import java.util.stream.Stream;
public abstract class BaseQuickstepLauncher extends Launcher
implements NavigationModeChangeListener {
protected SystemActions mSystemActions;
/**
* Reusable command for applying the back button alpha on the background thread.
*/
@@ -73,6 +78,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSystemActions = new SystemActions(this);
SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
.addModeChangeListener(this);
@@ -130,6 +136,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mSystemActions.onActivityResult(requestCode);
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
@@ -193,6 +205,15 @@ public abstract class BaseQuickstepLauncher extends Launcher
// removes the task itself.
startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
}
// Register all system actions once they are available
mSystemActions.register();
}
@Override
protected void onPause() {
super.onPause();
mSystemActions.unregister();
}
@Override
@@ -201,7 +222,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanelContainer().bringToFront();
getOverviewPanel().bringToFront();
if (getActionsView() != null) {
getActionsView().bringToFront();
}
}
}
@@ -217,9 +241,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
float offscreenTranslationX = getDeviceProfile().widthPx
- getOverviewPanel().getPaddingStart();
return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
PagedOrientationHandler layoutVertical =
((RecentsView)getOverviewPanel()).getPagedViewOrientedState().getOrientationHandler();
return layoutVertical.getScaleAndTranslation(getDeviceProfile(),
getOverviewPanel());
}
return super.getOverviewScaleAndTranslationForNormalState();
}
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.accessibility;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
/**
* Manages the launcher system actions presented to accessibility services.
*/
public class SystemActions {
/**
* System Action ID to show all apps. This ID should follow the ones in
* com.android.systemui.accessibility.SystemActions.
*/
private static final int SYSTEM_ACTION_ID_ALL_APPS = 100;
private Launcher mLauncher;
private AccessibilityManager mAccessibilityManager;
private RemoteAction mAllAppsAction;
private boolean mRegistered;
public SystemActions(Launcher launcher) {
mLauncher = launcher;
mAccessibilityManager = (AccessibilityManager) launcher.getSystemService(
Context.ACCESSIBILITY_SERVICE);
mAllAppsAction = new RemoteAction(
Icon.createWithResource(launcher, R.drawable.ic_apps),
launcher.getString(R.string.all_apps_label),
launcher.getString(R.string.all_apps_label),
launcher.createPendingResult(SYSTEM_ACTION_ID_ALL_APPS, new Intent(),
0 /* flags */));
}
public void register() {
if (mRegistered) {
return;
}
mAccessibilityManager.registerSystemAction(mAllAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
mRegistered = true;
}
public void unregister() {
if (!mRegistered) {
return;
}
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
mRegistered = false;
}
public void onActivityResult(int requestCode) {
if (requestCode == SYSTEM_ACTION_ID_ALL_APPS) {
showAllApps();
}
}
private void showAllApps() {
LauncherStateManager stateManager = mLauncher.getStateManager();
stateManager.goToState(NORMAL);
stateManager.goToState(ALL_APPS);
}
}
@@ -52,10 +52,12 @@ public abstract class BaseRecentsViewStateController<T extends View>
implements StateHandler {
protected final T mRecentsView;
protected final Launcher mLauncher;
protected final View mActionsView;
public BaseRecentsViewStateController(@NonNull Launcher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
mActionsView = launcher.getActionsView();
}
@Override
@@ -72,6 +74,10 @@ public abstract class BaseRecentsViewStateController<T extends View>
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
if (mActionsView != null) {
mActionsView.setTranslationX(translationX);
mActionsView.setAlpha(state.overviewUi ? 1f : 0);
}
}
@Override
@@ -118,6 +124,11 @@ public abstract class BaseRecentsViewStateController<T extends View>
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
if (mActionsView != null) {
setter.setFloat(mActionsView, View.TRANSLATION_X, translationX, translateXInterpolator);
setter.setFloat(mActionsView, View.ALPHA, toState.overviewUi ? 1 : 0,
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
}
}
/**
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -31,6 +32,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -49,6 +51,15 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
/**
* @return The progress of the swipe where we start resisting the user, where 0 is fullscreen
* and 1 is recents. These values should probably be greater than 1 to let the user swipe past
* recents before we start resisting them.
*/
default Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
return new Pair<>(1.4f, 1.8f);
}
void onSwipeUpToRecentsComplete();
default void onSwipeUpToHomeComplete() { }
@@ -148,13 +159,18 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
// No-op
}
static RectF getDefaultWindowTargetRect(DeviceProfile dp) {
static RectF getDefaultWindowTargetRect(PagedOrientationHandler orientationHandler,
DeviceProfile dp) {
final int halfIconSize = dp.iconSizePx / 2;
final float targetCenterX = dp.availableWidthPx / 2f;
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
float primaryDimension = orientationHandler
.getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
float secondaryDimension = orientationHandler
.getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
final float targetX = primaryDimension / 2f;
final float targetY = secondaryDimension - dp.hotseatBarSizePx;
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
return new RectF(targetX - halfIconSize, targetY - halfIconSize,
targetX + halfIconSize, targetY + halfIconSize);
}
}
@@ -19,10 +19,12 @@ import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import android.app.ActivityManager;
import android.content.Intent;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -281,4 +283,13 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mStateCallback.setState(STATE_RECENTS_ANIMATION_FINISHED);
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
}
public void dump(PrintWriter pw) {
pw.println("GestureState:");
pw.println(" gestureID=" + mGestureId);
pw.println(" runningTask=" + mRunningTask);
pw.println(" endTarget=" + mEndTarget);
pw.println(" finishingRecentsAnimationTaskId=" + mFinishingRecentsAnimationTaskId);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
}
@@ -0,0 +1,341 @@
/*
* 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_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.Surface;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.DefaultDisplay;
import java.io.PrintWriter;
/**
* Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
* See {@link OrientationRectF#applyTransform(MotionEvent, boolean)} for transformation of
* MotionEvents from one orientation's coordinate space to another's.
*
* This class only supports single touch/pointer gesture tracking for touches started in a supported
* nav bar region.
*/
class OrientationTouchTransformer {
private static final String TAG = "OrientationTouchTransformer";
private static final boolean DEBUG = false;
private static final int MAX_ORIENTATIONS = 4;
private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
private final RectF mAssistantLeftRegion = new RectF();
private final RectF mAssistantRightRegion = new RectF();
private int mCurrentRotation;
private boolean mEnableMultipleRegions;
private Resources mResources;
private OrientationRectF mLastRectTouched;
private SysUINavigationMode.Mode mMode;
private QuickStepContractInfo mContractInfo;
private int mQuickStepStartingRotation = -1;
/** For testability */
interface QuickStepContractInfo {
float getWindowCornerRadius();
}
OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode,
QuickStepContractInfo contractInfo) {
mResources = resources;
mMode = mode;
mContractInfo = contractInfo;
}
void setNavigationMode(SysUINavigationMode.Mode newMode) {
this.mMode = newMode;
}
/**
* Sets the current nav bar region to listen to events for as determined by
* {@param info}. If multiple nav bar regions are enabled, then this region will be added
* alongside other regions.
* Ok to call multiple times
*
* @see #enableMultipleRegions(boolean, DefaultDisplay.Info)
*/
void createOrAddTouchRegion(DefaultDisplay.Info info) {
mCurrentRotation = info.rotation;
if (mQuickStepStartingRotation > -1 && mCurrentRotation == mQuickStepStartingRotation) {
// Ignore nav bars in other rotations except for the one we started out in
resetSwipeRegions(info);
return;
}
OrientationRectF region = mSwipeTouchRegions.get(mCurrentRotation);
if (region != null) {
return;
}
if (mEnableMultipleRegions) {
mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(info));
} else {
resetSwipeRegions(info);
}
}
/**
* Call when we want to start tracking nav bar touch regions in multiple orientations.
* ALSO, you BETTER call this with {@param enableMultipleRegions} set to false once you're done.
*
* @param enableMultipleRegions Set to true to start tracking multiple nav bar regions
* @param info The current displayInfo
*/
void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) {
mEnableMultipleRegions = enableMultipleRegions;
if (!enableMultipleRegions) {
mQuickStepStartingRotation = -1;
resetSwipeRegions(info);
} else {
if (mQuickStepStartingRotation < 0) {
mQuickStepStartingRotation = mLastRectTouched.mRotation;
}
}
}
/**
* Only saves the swipe region represented by {@param region}, clears the
* rest from {@link #mSwipeTouchRegions}
* To be called whenever we want to stop tracking more than one swipe region.
* Ok to call multiple times.
*/
private void resetSwipeRegions(DefaultDisplay.Info region) {
if (DEBUG) {
Log.d(TAG, "clearing all regions except rotation: " + mCurrentRotation);
}
mCurrentRotation = region.rotation;
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentRotation);
mSwipeTouchRegions.clear();
mSwipeTouchRegions.put(mCurrentRotation,
regionToKeep != null ? regionToKeep : createRegionForDisplay(region));
}
private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) {
if (DEBUG) {
Log.d(TAG, "creating rotation region for: " + mCurrentRotation);
}
Point size = display.realSize;
int rotation = display.rotation;
OrientationRectF orientationRectF =
new OrientationRectF(0, 0, size.x, size.y, rotation);
if (mMode == SysUINavigationMode.Mode.NO_BUTTON) {
int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
orientationRectF.top = orientationRectF.bottom - touchHeight;
final int assistantWidth = mResources
.getDimensionPixelSize(R.dimen.gestures_assistant_width);
final float assistantHeight = Math.max(touchHeight,
mContractInfo.getWindowCornerRadius());
mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = orientationRectF.bottom;
mAssistantLeftRegion.top = mAssistantRightRegion.top =
orientationRectF.bottom - assistantHeight;
mAssistantLeftRegion.left = 0;
mAssistantLeftRegion.right = assistantWidth;
mAssistantRightRegion.right = orientationRectF.right;
mAssistantRightRegion.left = orientationRectF.right - assistantWidth;
} else {
mAssistantLeftRegion.setEmpty();
mAssistantRightRegion.setEmpty();
switch (rotation) {
case Surface.ROTATION_90:
orientationRectF.left = orientationRectF.right
- getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
break;
case Surface.ROTATION_270:
orientationRectF.right = orientationRectF.left
+ getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
break;
default:
orientationRectF.top = orientationRectF.bottom
- getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
}
}
return orientationRectF;
}
boolean touchInAssistantRegion(MotionEvent ev) {
return mAssistantLeftRegion.contains(ev.getX(), ev.getY())
|| mAssistantRightRegion.contains(ev.getX(), ev.getY());
}
private int getNavbarSize(String resName) {
return ResourceUtils.getNavbarSize(resName, mResources);
}
boolean touchInValidSwipeRegions(float x, float y) {
if (mLastRectTouched != null) {
return mLastRectTouched.contains(x, y);
}
return false;
}
int getCurrentActiveRotation() {
if (mLastRectTouched == null) {
return 0;
} else {
return mLastRectTouched.mRotation;
}
}
public void transform(MotionEvent event) {
int eventAction = event.getActionMasked();
switch (eventAction) {
case ACTION_MOVE: {
if (mLastRectTouched == null) {
return;
}
mLastRectTouched.applyTransform(event, true);
break;
}
case ACTION_CANCEL:
case ACTION_UP: {
if (mLastRectTouched == null) {
return;
}
mLastRectTouched.applyTransform(event, true);
mLastRectTouched = null;
break;
}
case ACTION_POINTER_DOWN:
case ACTION_DOWN: {
if (mLastRectTouched != null) {
return;
}
for (int i = 0; i < MAX_ORIENTATIONS; i++) {
OrientationRectF rect = mSwipeTouchRegions.get(i);
if (rect == null) {
continue;
}
if (rect.applyTransform(event, false)) {
mLastRectTouched = rect;
if (DEBUG) {
Log.d(TAG, "set active region: " + rect);
}
return;
}
}
break;
}
}
}
public void dump(PrintWriter pw) {
pw.println("OrientationTouchTransformerState: ");
pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
pw.println(" lastTouchedRegion=" + mLastRectTouched);
pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions);
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
for(int i = 0; i < mSwipeTouchRegions.size(); i++) {
OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i));
regions.append(rectF.mRotation).append(" ");
}
pw.println(regions.toString());
}
private class OrientationRectF extends RectF {
/**
* Delta to subtract width and height by because if we report the translated touch
* bounds as the width and height, calling {@link RectF#contains(float, float)} will
* be false
*/
private float maxDelta = 0.001f;
private int mRotation;
private float mHeight;
private float mWidth;
OrientationRectF(float left, float top, float right, float bottom, int rotation) {
super(left, top, right, bottom);
this.mRotation = rotation;
mHeight = bottom - maxDelta;
mWidth = right - maxDelta;
}
@Override
public String toString() {
String s = super.toString();
s += " rotation: " + mRotation;
return s;
}
boolean applyTransform(MotionEvent event, boolean forceTransform) {
// TODO(b/149658423): See if we can use RotationHelper.getRotationMatrix here
MotionEvent tmp = MotionEvent.obtain(event);
Matrix outMatrix = new Matrix();
int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation);
switch (delta) {
case Surface.ROTATION_0:
outMatrix.reset();
break;
case Surface.ROTATION_90:
outMatrix.setRotate(270);
outMatrix.postTranslate(0, mHeight);
break;
case Surface.ROTATION_180:
outMatrix.setRotate(180);
outMatrix.postTranslate(mHeight, mWidth);
break;
case Surface.ROTATION_270:
outMatrix.setRotate(90);
outMatrix.postTranslate(mWidth, 0);
break;
}
tmp.transform(outMatrix);
if (DEBUG) {
Log.d(TAG, "original: " + event.getX() + ", " + event.getY()
+ " new: " + tmp.getX() + ", " + tmp.getY()
+ " rect: " + this + " forceTransform: " + forceTransform
+ " contains: " + contains(tmp.getX(), tmp.getY()));
}
if (forceTransform || contains(tmp.getX(), tmp.getY())) {
event.transform(outMatrix);
tmp.recycle();
return true;
}
return false;
}
}
}
@@ -33,6 +33,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.SparseIntArray;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.io.PrintWriter;
@@ -44,18 +45,11 @@ import java.util.Objects;
* and provide callers the relevant classes.
*/
public final class OverviewComponentObserver {
private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateOverviewTargets();
}
};
private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateOverviewTargets();
}
};
private final BroadcastReceiver mUserPreferenceChangeReceiver =
new SimpleBroadcastReceiver(this::updateOverviewTargets);
private final BroadcastReceiver mOtherHomeAppUpdateReceiver =
new SimpleBroadcastReceiver(this::updateOverviewTargets);
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
private final Intent mCurrentHomeIntent;
@@ -106,6 +100,10 @@ public final class OverviewComponentObserver {
}
}
private void updateOverviewTargets(Intent unused) {
updateOverviewTargets();
}
/**
* Update overview intent and {@link BaseActivityInterface} based off the current launcher home
* component.
@@ -131,11 +129,8 @@ public final class OverviewComponentObserver {
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
mUpdateRegisteredPackage = null;
}
// Remove any update listener as we don't care about other packages.
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
@@ -149,13 +144,9 @@ public final class OverviewComponentObserver {
// Listen for package updates of this app (and remove any previously attached
// package listener).
if (defaultHome == null) {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
}
unregisterOtherHomeAppUpdateReceiver();
} else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
}
unregisterOtherHomeAppUpdateReceiver();
mUpdateRegisteredPackage = defaultHome.getPackageName();
mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
@@ -170,7 +161,10 @@ public final class OverviewComponentObserver {
*/
public void onDestroy() {
mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
unregisterOtherHomeAppUpdateReceiver();
}
private void unregisterOtherHomeAppUpdateReceiver() {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
mUpdateRegisteredPackage = null;
@@ -39,7 +39,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Wrapper around RecentsAnimationController to help with some synchronization
* Wrapper around RecentsAnimationControllerCompat to help with some synchronization
*/
public class RecentsAnimationController {
@@ -17,8 +17,6 @@ package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -40,20 +38,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Process;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import androidx.annotation.BinderThread;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
@@ -63,6 +58,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -80,6 +76,7 @@ public class RecentsAnimationDeviceState implements
private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay;
private final int mDisplayId;
private int mDisplayRotation;
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
@@ -87,10 +84,7 @@ public class RecentsAnimationDeviceState implements
private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
private NavBarPosition mNavBarPosition;
private final RectF mSwipeUpTouchRegion = new RectF();
private final Region mDeferredGestureRegion = new Region();
private final RectF mAssistantLeftRegion = new RectF();
private final RectF mAssistantRightRegion = new RectF();
private boolean mAssistantAvailable;
private float mAssistantVisibility;
@@ -106,10 +100,13 @@ public class RecentsAnimationDeviceState implements
}
};
private OrientationTouchTransformer mOrientationTouchTransformer;
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
private final List<ComponentName> mGestureBlockedActivities;
private TaskStackChangeListener mFrozenTaskListener;
public RecentsAnimationDeviceState(Context context) {
final ContentResolver resolver = context.getContentResolver();
@@ -139,6 +136,8 @@ public class RecentsAnimationDeviceState implements
};
runOnDestroy(mExclusionListener::unregister);
setupOrientationSwipeHandler(context);
// Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
@@ -160,6 +159,26 @@ public class RecentsAnimationDeviceState implements
}
}
private void setupOrientationSwipeHandler(Context context) {
final Resources resources = context.getResources();
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
() -> QuickStepContract.getWindowCornerRadius(resources));
if (!PagedView.sFlagForcedRotation) {
return;
}
mFrozenTaskListener = new TaskStackChangeListener() {
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
mOrientationTouchTransformer.enableMultipleRegions(frozen, mDefaultDisplay.getInfo());
}
};
ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
runOnDestroy(() -> ActivityManagerWrapper.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener));
}
private void runOnDestroy(Runnable action) {
mOnDestroyActions.add(action);
}
@@ -198,7 +217,10 @@ public class RecentsAnimationDeviceState implements
mExclusionListener.unregister();
}
mMode = newMode;
mNavBarPosition = new NavBarPosition(mMode, mDefaultDisplay.getInfo());
mOrientationTouchTransformer.setNavigationMode(mMode);
}
@Override
@@ -207,8 +229,10 @@ public class RecentsAnimationDeviceState implements
return;
}
mDisplayRotation = info.rotation;
mNavBarPosition = new NavBarPosition(mMode, info);
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
}
/**
@@ -380,50 +404,14 @@ public class RecentsAnimationDeviceState implements
return;
}
Resources res = mContext.getResources();
DefaultDisplay.Info displayInfo = mDefaultDisplay.getInfo();
Point realSize = new Point(displayInfo.realSize);
mSwipeUpTouchRegion.set(0, 0, realSize.x, realSize.y);
if (mMode == NO_BUTTON) {
int touchHeight = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res);
mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom - touchHeight;
final int assistantWidth = res.getDimensionPixelSize(R.dimen.gestures_assistant_width);
final float assistantHeight = Math.max(touchHeight,
QuickStepContract.getWindowCornerRadius(res));
mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeUpTouchRegion.bottom;
mAssistantLeftRegion.top = mAssistantRightRegion.top =
mSwipeUpTouchRegion.bottom - assistantHeight;
mAssistantLeftRegion.left = 0;
mAssistantLeftRegion.right = assistantWidth;
mAssistantRightRegion.right = mSwipeUpTouchRegion.right;
mAssistantRightRegion.left = mSwipeUpTouchRegion.right - assistantWidth;
} else {
mAssistantLeftRegion.setEmpty();
mAssistantRightRegion.setEmpty();
switch (displayInfo.rotation) {
case Surface.ROTATION_90:
mSwipeUpTouchRegion.left = mSwipeUpTouchRegion.right
- ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res);
break;
case Surface.ROTATION_270:
mSwipeUpTouchRegion.right = mSwipeUpTouchRegion.left
+ ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res);
break;
default:
mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom
- ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res);
}
}
mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
}
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event) {
return mSwipeUpTouchRegion.contains(event.getX(), event.getY());
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
}
/**
@@ -431,7 +419,8 @@ public class RecentsAnimationDeviceState implements
* is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
return mSwipeUpTouchRegion.contains(event.getX(pointerIndex), event.getY(pointerIndex));
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
event.getY(pointerIndex));
}
/**
@@ -490,11 +479,34 @@ public class RecentsAnimationDeviceState implements
public boolean canTriggerAssistantAction(MotionEvent ev) {
return mAssistantAvailable
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
&& (mAssistantLeftRegion.contains(ev.getX(), ev.getY())
|| mAssistantRightRegion.contains(ev.getX(), ev.getY()))
&& mOrientationTouchTransformer.touchInAssistantRegion(ev)
&& !isLockToAppActive();
}
/**
* *May* apply a transform on the motion event if it lies in the nav bar region for another
* orientation that is currently being tracked as a part of quickstep
*/
public void setOrientationTransformIfNeeded(MotionEvent event) {
// negative coordinates bug b/143901881
if (event.getX() < 0 || event.getY() < 0) {
event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
}
mOrientationTouchTransformer.transform(event);
}
public void enableMultipleRegions(boolean enable) {
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
}
public int getCurrentActiveRotation() {
return mOrientationTouchTransformer.getCurrentActiveRotation();
}
public int getDisplayRotation() {
return mDisplayRotation;
}
public void dump(PrintWriter pw) {
pw.println("DeviceState:");
pw.println(" canStartSystemGesture=" + canStartSystemGesture());
@@ -504,5 +516,8 @@ public class RecentsAnimationDeviceState implements
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
pw.println(" displayRotation=" + getDisplayRotation());
mOrientationTouchTransformer.dump(pw);
}
}
@@ -333,4 +333,15 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
}
public void onQuickSwitchToNewTask() {
//TODO(b/150250451) add back in after big CL goes through
// if (mSystemUiProxy != null) {
// try {
// mSystemUiProxy.onQuickSwitchToNewTask();
// } catch (RemoteException e) {
// Log.w(TAG, "Failed call onQuickstepStarted", e);
// }
// }
}
}
@@ -33,6 +33,9 @@ import com.android.quickstep.SysUINavigationMode;
import java.lang.annotation.Retention;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
public class LayoutUtils {
private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertTrue;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
import android.os.Build;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -33,6 +34,9 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
@Test
public void testToast() throws Exception {
// b/150303529
if (Build.MODEL.contains("Cuttlefish")) return;
startAppFast(CALCULATOR_PACKAGE);
final UsageStatsManager usageStatsManager =
@@ -17,8 +17,6 @@
package com.android.quickstep;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -41,7 +39,6 @@ import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
@@ -254,8 +251,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
@NavigationModeSwitch
@PortraitLandscape
// b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
@TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
+7 -2
View File
@@ -44,8 +44,13 @@
layout="@layout/hotseat" />
<include
android:id="@+id/overview_panel_container"
layout="@layout/overview_panel"/>
android:id="@+id/overview_panel"
layout="@layout/overview_panel"
android:visibility="gone" />
<include
android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_holder" />
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Space
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp" />
+4 -6
View File
@@ -14,9 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overview_panel_recents"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone" />
<Space
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp" />
+53 -1
View File
@@ -121,5 +121,57 @@
<bool name="config_enableTaskSnapshotPreloading">true</bool>
<!-- Configuration resources -->
<array name="dynamic_resources"> </array>
<item name="all_apps_spring_damping_ratio" type="dimen" format="float">0.75</item>
<item name="all_apps_spring_stiffness" type="dimen" format="float">600</item>
<item name="dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.5</item>
<item name="dismiss_task_trans_y_stiffness" type="dimen" format="float">1500</item>
<item name="dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.5</item>
<item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">1500</item>
<item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.75</item>
<item name="horizontal_spring_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
<item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
<item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
<!-- Swipe up to home related -->
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_y_overshoot">10dp</dimen>
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
<array name="dynamic_resources">
<item>@dimen/all_apps_spring_damping_ratio</item>
<item>@dimen/all_apps_spring_stiffness</item>
<item>@dimen/dismiss_task_trans_y_damping_ratio</item>
<item>@dimen/dismiss_task_trans_y_stiffness</item>
<item>@dimen/dismiss_task_trans_x_damping_ratio</item>
<item>@dimen/dismiss_task_trans_x_stiffness</item>
<item>@dimen/horizontal_spring_damping_ratio</item>
<item>@dimen/horizontal_spring_stiffness</item>
<item>@dimen/swipe_up_rect_scale_damping_ratio</item>
<item>@dimen/swipe_up_rect_scale_stiffness</item>
<item>@dimen/swipe_up_rect_xy_fling_friction</item>
<item>@dimen/swipe_up_rect_xy_damping_ratio</item>
<item>@dimen/swipe_up_rect_xy_stiffness</item>
<item>@dimen/staggered_damping_ratio</item>
<item>@dimen/staggered_stiffness</item>
<item>@dimen/swipe_up_fling_min_visible_change</item>
<item>@dimen/swipe_up_y_overshoot</item>
</array>
</resources>
+20
View File
@@ -2783,6 +2783,26 @@ public class CellLayout extends ViewGroup implements Transposable {
return false;
}
/**
* Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig
*/
public boolean makeSpaceForHotseatMigration(boolean commitConfig) {
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false;
int[] cellPoint = new int[2];
int[] directionVector = new int[]{0, -1};
cellToPoint(0, mCountY, cellPoint);
ItemConfiguration configuration = new ItemConfiguration();
if (findReorderSolution(cellPoint[0], cellPoint[1], mCountX, 1, mCountX, 1,
directionVector, null, false, configuration).isSolution) {
if (commitConfig) {
copySolutionToTempState(configuration, null);
commitTempPlacement();
}
return true;
}
return false;
}
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
return mOccupied.isRegionVacant(x, y, spanX, spanY);
}
+19 -7
View File
@@ -80,7 +80,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -274,7 +273,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// UI and state for the overview panel
private View mOverviewPanel;
private FrameLayout mOverviewPanelContainer;
private View mActionsView;
@Thunk
boolean mWorkspaceLoading = true;
@@ -834,6 +833,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
}
mDragLayer.clearAnimatedView();
}
@@ -1162,8 +1162,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mOverviewPanel = findViewById(R.id.overview_panel_recents);
mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
mOverviewPanel = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
mHotseat = findViewById(R.id.hotseat);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1363,7 +1363,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
protected void onUiChangedWhileSleeping() { }
public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mWorkspace.updateNotificationDots(updatedDots);
mAppsView.getAppsStore().updateNotificationDots(updatedDots);
}
@@ -1411,8 +1411,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return (T) mOverviewPanel;
}
public FrameLayout getOverviewPanelContainer() {
return mOverviewPanelContainer;
public View getActionsView() {
return mActionsView;
}
public DropTargetBar getDropTargetBar() {
@@ -2327,6 +2327,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
getModelWriter().updateItemInDatabase(item);
}
else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
&& appWidgetInfo.configure != null) {
if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) {
item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
getModelWriter().updateItemInDatabase(item);
}
}
}
if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
@@ -2340,6 +2347,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
item.minSpanX = appWidgetInfo.minSpanX;
item.minSpanY = appWidgetInfo.minSpanY;
view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
} else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
&& appWidgetInfo != null) {
mAppWidgetHost.addPendingView(item.appWidgetId,
new PendingAppWidgetHostView(this, item, mIconCache, false));
view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
} else {
view = new PendingAppWidgetHostView(this, item, mIconCache, false);
}
@@ -32,6 +32,7 @@ import android.widget.Toast;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.widget.DeferredAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
@@ -53,12 +54,14 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
private final Context mContext;
private int mFlags = FLAG_RESUMED;
private IntConsumer mAppWidgetRemovedCallback = null;
public LauncherAppWidgetHost(Context context) {
this(context, null);
}
@@ -73,7 +76,13 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
final LauncherAppWidgetHostView view;
if (mPendingViews.get(appWidgetId) != null) {
view = mPendingViews.get(appWidgetId);
mPendingViews.remove(appWidgetId);
} else {
view = new LauncherAppWidgetHostView(context);
}
mViews.put(appWidgetId, view);
return view;
}
@@ -189,6 +198,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
}
void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
mPendingViews.put(appWidgetId, view);
}
public AppWidgetHostView createView(Context context, int appWidgetId,
LauncherAppWidgetProviderInfo appWidget) {
if (appWidget.isCustomWidget()) {
@@ -238,8 +251,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
/**
* Called on an appWidget is removed for a widgetId
* @param appWidgetId
* TODO: make this override when SDK is updated
*
* @param appWidgetId TODO: make this override when SDK is updated
*/
public void onAppWidgetRemoved(int appWidgetId) {
if (mAppWidgetRemovedCallback == null) {
+222 -167
View File
@@ -16,11 +16,6 @@
package com.android.launcher3;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import android.animation.LayoutTransition;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
@@ -35,6 +30,7 @@ import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
@@ -49,13 +45,26 @@ import android.widget.ScrollView;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
/**
* An abstraction of the original Workspace which supports browsing through a
* sequential list of "pages"
@@ -64,6 +73,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
private static final String TAG = "PagedView";
private static final boolean DEBUG = false;
public static boolean sFlagForcedRotation = false;
public static final int INVALID_PAGE = -1;
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
@@ -97,8 +108,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@ViewDebug.ExportedProperty(category = "launcher")
protected int mNextPage = INVALID_PAGE;
protected int mMinScrollX;
protected int mMaxScrollX;
protected int mMaxScroll;
protected int mMinScroll;
protected OverScroller mScroller;
private Interpolator mDefaultInterpolator;
private VelocityTracker mVelocityTracker;
@@ -106,9 +117,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
private float mDownMotionX;
private float mDownMotionY;
private float mLastMotionX;
private float mLastMotionXRemainder;
private float mTotalMotionX;
private float mDownMotionPrimary;
private float mLastMotion;
private float mLastMotionRemainder;
private float mTotalMotion;
protected PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler();
protected final PagedViewOrientedState mOrientationState = new PagedViewOrientedState();
protected int[] mPageScrolls;
private boolean mIsBeingDragged;
@@ -123,11 +137,14 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean mIsPageInTransition = false;
protected float mSpringOverScrollX;
protected float mSpringOverScroll;
protected boolean mWasInOverscroll = false;
protected int mUnboundedScrollX;
protected int mUnboundedScroll;
protected int mLayoutRotation = Surface.ROTATION_0;
protected int mDisplayRotation = Surface.ROTATION_0;
// Page Indicator
@Thunk int mPageIndicatorViewId;
@@ -166,11 +183,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
* Initializes various states for this workspace.
*/
protected void init() {
mScroller = new OverScroller(getContext());
Context context = getContext();
mScroller = new OverScroller(context);
setDefaultInterpolator(Interpolators.SCROLL);
mCurrentPage = 0;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledPagingTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
@@ -182,6 +200,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (Utilities.ATLEAST_OREO) {
setDefaultFocusHighlightEnabled(false);
}
sFlagForcedRotation = Utilities.isForcedRotation(context);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -236,12 +256,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
*/
protected void updateCurrentPageScroll() {
// If the current page is invalid, just reset the scroll position to zero
int newX = 0;
int newPosition = 0;
if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
newX = getScrollForPage(mCurrentPage);
newPosition = getScrollForPage(mCurrentPage);
}
scrollTo(newX, 0);
mScroller.startScroll(mScroller.getCurrPos(), newX - mScroller.getCurrPos());
mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
mOrientationHandler.scrollerStartScroll(mScroller, newPosition);
forceFinishScroller(true);
}
@@ -285,7 +305,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
int dir = !mIsRtl ? 1 : - 1;
int currScroll = getScrollForPage(page);
int prevScroll;
while (currScroll < mMinScrollX) {
while (currScroll < mMinScroll) {
page += dir;
prevScroll = currScroll;
currScroll = getScrollForPage(page);
@@ -294,7 +314,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
break;
}
}
while (currScroll > mMaxScrollX) {
while (currScroll > mMaxScroll) {
page -= dir;
prevScroll = currScroll;
currScroll = getScrollForPage(page);
@@ -378,45 +398,73 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
protected int getUnboundedScrollX() {
return mUnboundedScrollX;
protected int getUnboundedScroll() {
return mUnboundedScroll;
}
protected void updateLayoutRotation(int touchRotation) {
setLayoutRotation(touchRotation, mDisplayRotation);
}
/** @param touchRotation Must be one of {@link android.view.Surface.ROTATION_0/90/180/270} */
public void setLayoutRotation(int touchRotation, int displayRotation) {
if (mLayoutRotation == touchRotation && mDisplayRotation == displayRotation) {
return;
}
mOrientationState.update(touchRotation, displayRotation);
mOrientationHandler = mOrientationState.getOrientationHandler();
mLayoutRotation = touchRotation;
mDisplayRotation = displayRotation;
requestLayout();
}
public PagedViewOrientedState getPagedViewOrientedState() {
return mOrientationState;
}
public PagedOrientationHandler getPagedOrientationHandler() {
return getPagedViewOrientedState().getOrientationHandler();
}
public void disableMultipleLayoutRotations(boolean disable) {
mOrientationState.disableMultipleOrientations(disable);
mOrientationHandler = mOrientationState.getOrientationHandler();
requestLayout();
}
@Override
public void scrollBy(int x, int y) {
scrollTo(getUnboundedScrollX() + x, getScrollY() + y);
mOrientationHandler.delegateScrollBy(this, getUnboundedScroll(), x, y);
}
@Override
public void scrollTo(int x, int y) {
mUnboundedScrollX = x;
int primaryScroll = mOrientationHandler.getPrimaryValue(x, y);
int secondaryScroll = mOrientationHandler.getSecondaryValue(x, y);
mUnboundedScroll = primaryScroll;
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX);
boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX);
if (!isXBeforeFirstPage && !isXAfterLastPage) {
mSpringOverScrollX = 0;
boolean isBeforeFirstPage = mIsRtl ?
(primaryScroll > mMaxScroll) : (primaryScroll < mMinScroll);
boolean isAfterLastPage = mIsRtl ?
(primaryScroll < mMinScroll) : (primaryScroll > mMaxScroll);
if (!isBeforeFirstPage && !isAfterLastPage) {
mSpringOverScroll = 0;
}
if (isXBeforeFirstPage) {
super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y);
if (isBeforeFirstPage) {
mOrientationHandler.delegateScrollTo(this,
secondaryScroll, mIsRtl ? mMaxScroll : mMinScroll);
if (mAllowOverScroll) {
mWasInOverscroll = true;
if (mIsRtl) {
overScroll(x - mMaxScrollX);
} else {
overScroll(x - mMinScrollX);
}
overScroll(primaryScroll - (mIsRtl ? mMaxScroll : mMinScroll));
}
} else if (isXAfterLastPage) {
super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y);
} else if (isAfterLastPage) {
mOrientationHandler.delegateScrollTo(this,
secondaryScroll, mIsRtl ? mMinScroll : mMaxScroll);
if (mAllowOverScroll) {
mWasInOverscroll = true;
if (mIsRtl) {
overScroll(x - mMinScrollX);
} else {
overScroll(x - mMaxScrollX);
}
overScroll(primaryScroll - (mIsRtl ? mMinScroll : mMaxScroll));
}
} else {
if (mWasInOverscroll) {
@@ -425,7 +473,13 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
super.scrollTo(x, y);
}
}
/**
* Helper for {@link PagedOrientationHandler} to be able to call parent's scrollTo method
*/
public void superScrollTo(int x, int y) {
super.scrollTo(x, y);
}
private void sendScrollAccessibilityEvent() {
@@ -436,9 +490,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
ev.setScrollable(true);
ev.setScrollX(getScrollX());
ev.setScrollY(getScrollY());
ev.setMaxScrollX(mMaxScrollX);
ev.setMaxScrollY(0);
mOrientationHandler.setMaxScroll(ev, mMaxScroll);
sendAccessibilityEventUnchecked(ev);
}
}
@@ -459,9 +511,10 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean computeScrollHelper(boolean shouldInvalidate) {
if (mScroller.computeScrollOffset()) {
// Don't bother scrolling if the page does not need to be moved
if (getUnboundedScrollX() != mScroller.getCurrPos()
|| getScrollX() != mScroller.getCurrPos()) {
scrollTo(mScroller.getCurrPos(), 0);
int currentScroll = mOrientationHandler.getPrimaryScroll(this);
if (mUnboundedScroll != mScroller.getCurrPos()
|| currentScroll != mScroller.getCurrPos()) {
mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrPos());
}
if (shouldInvalidate) {
invalidate();
@@ -580,7 +633,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) {
boolean isScrollChanged = getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC);
if (isScrollChanged) {
pageScrollChanged = true;
}
@@ -621,7 +675,6 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
/**
* Initializes {@code outPageScrolls} with scroll positions for view at that index. The length
* of {@code outPageScrolls} should be same as the the childCount
*
*/
protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
ComputePageScrollsLogic scrollLogic) {
@@ -631,36 +684,30 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
final int endIndex = mIsRtl ? -1 : childCount;
final int delta = mIsRtl ? -1 : 1;
final int verticalCenter = (getPaddingTop() + getMeasuredHeight() + mInsets.top
- mInsets.bottom - getPaddingBottom()) / 2;
final int pageCenter = mOrientationHandler.getCenterForPage(this, mInsets);
final int scrollOffsetLeft = mInsets.left + getPaddingLeft();
final int scrollOffsetRight = getWidth() - getPaddingRight() - mInsets.right;
final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets);
final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets);
boolean pageScrollChanged = false;
for (int i = startIndex, childLeft = scrollOffsetLeft; i != endIndex; i += delta) {
for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) {
final View child = getPageAt(i);
if (scrollLogic.shouldIncludeView(child)) {
final int childWidth = child.getMeasuredWidth();
final int childRight = childLeft + childWidth;
if (layoutChildren) {
final int childHeight = child.getMeasuredHeight();
final int childTop = verticalCenter - childHeight / 2;
child.layout(childLeft, childTop, childRight, childTop + childHeight);
}
ChildBounds bounds = mOrientationHandler.getChildBounds(child, childStart,
pageCenter, layoutChildren);
final int primaryDimension = bounds.primaryDimension;
final int childPrimaryEnd = bounds.childPrimaryEnd;
// In case the pages are of different width, align the page to left or right edge
// based on the orientation.
final int pageScroll = mIsRtl
? (childLeft - scrollOffsetLeft)
: Math.max(0, childRight - scrollOffsetRight);
? (childStart - scrollOffsetStart)
: Math.max(0, childPrimaryEnd - scrollOffsetEnd);
if (outPageScrolls[i] != pageScroll) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
}
childLeft += childWidth + mPageSpacing + getChildGap();
childStart += primaryDimension + mPageSpacing + getChildGap();
}
}
return pageScrollChanged;
@@ -671,15 +718,15 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
protected void updateMinAndMaxScrollX() {
mMinScrollX = computeMinScrollX();
mMaxScrollX = computeMaxScrollX();
mMinScroll = computeMinScroll();
mMaxScroll = computeMaxScroll();
}
protected int computeMinScrollX() {
protected int computeMinScroll() {
return 0;
}
protected int computeMaxScrollX() {
protected int computeMaxScroll() {
int childCount = getChildCount();
if (childCount > 0) {
final int index = mIsRtl ? 0 : childCount - 1;
@@ -722,7 +769,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected int getChildOffset(int index) {
if (index < 0 || index > getChildCount() - 1) return 0;
return getPageAt(index).getLeft();
View pageAtIndex = getPageAt(index);
return mOrientationHandler.getChildStart(pageAtIndex);
}
@Override
@@ -873,13 +921,13 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
case MotionEvent.ACTION_MOVE: {
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
* whether the user has moved far enough from their original down touch.
*/
if (mActivePointerId != INVALID_POINTER) {
determineScrollingStart(ev);
}
// if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
// event. in that case, treat the first occurence of a move event as a ACTION_DOWN
// event. in that case, treat the first occurrence of a move event as a ACTION_DOWN
// i.e. fall through to the next case (don't break)
// (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
// while it's small- this was causing a crash before we checked for INVALID_POINTER)
@@ -892,9 +940,9 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
// Remember location of down touch
mDownMotionX = x;
mDownMotionY = y;
mLastMotionX = x;
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
mLastMotionRemainder = 0;
mTotalMotion = 0;
mActivePointerId = ev.getPointerId(0);
updateIsBeingDraggedOnTouchDown();
@@ -956,17 +1004,17 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) return;
final float x = ev.getX(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, pointerIndex);
final int diff = (int) Math.abs(primaryDirection - mLastMotion);
final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
boolean xMoved = xDiff > touchSlop;
boolean moved = diff > touchSlop;
if (xMoved) {
if (moved) {
// Scroll if the user moved far enough along the X axis
mIsBeingDragged = true;
mTotalMotionX += Math.abs(mLastMotionX - x);
mLastMotionX = x;
mLastMotionXRemainder = 0;
mTotalMotion += Math.abs(mLastMotion - primaryDirection);
mLastMotion = primaryDirection;
mLastMotionRemainder = 0;
onScrollInteractionBegin();
pageBeginTransition();
// Stop listening for things like pinches.
@@ -1033,10 +1081,9 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@Override
protected void dispatchDraw(Canvas canvas) {
if (mScroller.isSpringing() && mSpringOverScrollX != 0) {
if (mScroller.isSpringing() && mSpringOverScroll != 0) {
int saveCount = canvas.save();
canvas.translate(-mSpringOverScrollX, 0);
mOrientationHandler.set(canvas, CANVAS_TRANSLATE, -mSpringOverScroll);
super.dispatchDraw(canvas);
canvas.restoreToCount(saveCount);
@@ -1046,25 +1093,27 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
protected void dampedOverScroll(int amount) {
mSpringOverScrollX = amount;
mSpringOverScroll = amount;
if (amount == 0) {
return;
}
int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
mSpringOverScrollX = overScrollAmount;
int size = mOrientationHandler.getMeasuredSize(this);
int overScrollAmount = OverScroll.dampedScroll(amount, size);
mSpringOverScroll = overScrollAmount;
if (mScroller.isSpringing()) {
invalidate();
return;
}
int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX);
super.scrollTo(x + overScrollAmount, getScrollY());
int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
int boundedScroll = Utilities.boundToRange(primaryScroll, mMinScroll, mMaxScroll);
mOrientationHandler.delegateScrollTo(this, boundedScroll + overScrollAmount);
invalidate();
}
protected void overScroll(int amount) {
mSpringOverScrollX = amount;
mSpringOverScroll = amount;
if (mScroller.isSpringing()) {
invalidate();
return;
@@ -1073,11 +1122,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (amount == 0) return;
if (mFreeScroll && !mScroller.isFinished()) {
if (amount < 0) {
super.scrollTo(mMinScrollX + amount, getScrollY());
} else {
super.scrollTo(mMaxScrollX + amount, getScrollY());
}
int scrollAmount = amount < 0 ? mMinScroll + amount : mMaxScroll + amount;
mOrientationHandler.delegateScrollTo(this, scrollAmount);
} else {
dampedOverScroll(amount);
}
@@ -1127,37 +1173,37 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
mDownMotionX = ev.getX();
mDownMotionY = ev.getY();
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
mLastMotionRemainder = 0;
mTotalMotion = 0;
mActivePointerId = ev.getPointerId(0);
if (mIsBeingDragged) {
onScrollInteractionBegin();
pageBeginTransition();
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) return true;
final float x = ev.getX(pointerIndex);
final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
mTotalMotionX += Math.abs(deltaX);
float direction = mOrientationHandler.getPrimaryDirection(ev, pointerIndex);
float delta = mLastMotion + mLastMotionRemainder - direction;
mTotalMotion += Math.abs(delta);
// Only scroll and update mLastMotionX if we have moved some discrete amount. We
// keep the remainder because we are actually testing if we've moved from the last
// scrolled position (which is discrete).
if (Math.abs(deltaX) >= 1.0f) {
scrollBy((int) deltaX, 0);
mLastMotionX = x;
mLastMotionXRemainder = deltaX - (int) deltaX;
if (Math.abs(delta) >= 1.0f) {
mLastMotion = direction;
mLastMotionRemainder = delta - (int) delta;
mOrientationHandler.set(this, VIEW_SCROLL_BY, (int) delta);
} else {
awakenScrollBars();
}
@@ -1170,27 +1216,31 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (mIsBeingDragged) {
final int activePointerId = mActivePointerId;
final int pointerIndex = ev.findPointerIndex(activePointerId);
final float x = ev.getX(pointerIndex);
final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev,
pointerIndex);
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
final int deltaX = (int) (x - mDownMotionX);
final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth();
boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
SIGNIFICANT_MOVE_THRESHOLD;
mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
boolean isFling = mTotalMotionX > mTouchSlop && shouldFlingForVelocity(velocityX);
boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0;
boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0;
int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker,
mActivePointerId);
int delta = (int) (primaryDirection - mDownMotionPrimary);
int pageOrientedSize = mOrientationHandler.getMeasuredSize(getPageAt(mCurrentPage));
boolean isSignificantMove = Math.abs(delta) > pageOrientedSize *
SIGNIFICANT_MOVE_THRESHOLD;
mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection);
boolean isFling = mTotalMotion > mTouchSlop && shouldFlingForVelocity(velocity);
boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0;
boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0;
if (!mFreeScroll) {
// In the case that the page is moved far to one direction and then is flung
// in the opposite direction, we use a threshold to determine whether we should
// just return to the starting page, or if we should skip one further.
boolean returnToOriginalPage = false;
if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
if (Math.abs(delta) > pageOrientedSize * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
Math.signum(velocity) != Math.signum(delta) && isFling) {
returnToOriginalPage = true;
}
@@ -1199,15 +1249,15 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
// test for a large move if a fling has been registered. That is, a large
// move to the left and fling to the right will register as a fling to the right.
if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
(isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
if (((isSignificantMove && !isDeltaLeft && !isFling) ||
(isFling && !isVelocityLeft)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
snapToPageWithVelocity(finalPage, velocityX);
} else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
(isFling && isVelocityXLeft)) &&
snapToPageWithVelocity(finalPage, velocity);
} else if (((isSignificantMove && isDeltaLeft && !isFling) ||
(isFling && isVelocityLeft)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
snapToPageWithVelocity(finalPage, velocityX);
snapToPageWithVelocity(finalPage, velocity);
} else {
snapToDestination();
}
@@ -1216,38 +1266,40 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
abortScrollerAnimation(true);
}
int initialScrollX = getScrollX();
int initialScroll = mOrientationHandler.getPrimaryScroll(this);
int maxScroll = mMaxScroll;
int minScroll = mMinScroll;
if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) ||
((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) {
mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX);
if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) ||
((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) {
mScroller.springBack(initialScroll, minScroll, maxScroll);
mNextPage = getPageNearestToCenterOfScreen();
} else {
mScroller.setInterpolator(mDefaultInterpolator);
mScroller.fling(initialScrollX, -velocityX,
mMinScrollX, mMaxScrollX,
Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
mScroller.fling(initialScroll, -velocity,
minScroll, maxScroll,
Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
int finalX = mScroller.getFinalPos();
mNextPage = getPageNearestToCenterOfScreen(finalX);
int finalPos = mScroller.getFinalPos();
mNextPage = getPageNearestToCenterOfScreen(finalPos);
int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
if (finalX > mMinScrollX && finalX < mMaxScrollX) {
if (finalPos > minScroll && finalPos < maxScroll) {
// If scrolling ends in the half of the added space that is closer to
// the end, settle to the end. Otherwise snap to the nearest page.
// If flinging past one of the ends, don't change the velocity as it
// will get stopped at the end anyway.
int pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2
? mMinScrollX
: finalX > (lastPageScroll + mMaxScrollX) / 2
? mMaxScrollX
: getScrollForPage(mNextPage);
int pageSnapped = finalPos < (firstPageScroll + minScroll) / 2
? minScroll
: finalPos > (lastPageScroll + maxScroll) / 2
? maxScroll
: getScrollForPage(mNextPage);
mScroller.setFinalPos(pageSnappedX);
mScroller.setFinalPos(pageSnapped);
// Ensure the scroll/snap doesn't happen too fast;
int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
- mScroller.getDuration();
- mScroller.getDuration();
if (extraScrollDuration > 0) {
mScroller.extendDuration(extraScrollDuration);
}
@@ -1279,8 +1331,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
return true;
}
protected boolean shouldFlingForVelocity(int velocityX) {
return Math.abs(velocityX) > mFlingThresholdVelocity;
protected boolean shouldFlingForVelocity(int velocity) {
return Math.abs(velocity) > mFlingThresholdVelocity;
}
private void resetTouchState() {
@@ -1364,8 +1416,9 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
// active pointer and adjust accordingly.
// TODO: Make this decision more intelligent.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
mLastMotionXRemainder = 0;
mLastMotion = mDownMotionPrimary = mOrientationHandler.getPrimaryDirection(ev,
newPointerIndex);
mLastMotionRemainder = 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
@@ -1383,19 +1436,20 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
public int getPageNearestToCenterOfScreen() {
return getPageNearestToCenterOfScreen(getScrollX());
return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this));
}
private int getPageNearestToCenterOfScreen(int scaledScrollX) {
int screenCenter = scaledScrollX + (getMeasuredWidth() / 2);
private int getPageNearestToCenterOfScreen(int scaledScroll) {
int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
int screenCenter = scaledScroll + (pageOrientationSize / 2);
int minDistanceFromScreenCenter = Integer.MAX_VALUE;
int minDistanceFromScreenCenterIndex = -1;
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View layout = getPageAt(i);
int childWidth = layout.getMeasuredWidth();
int halfChildWidth = (childWidth / 2);
int childCenter = getChildOffset(i) + halfChildWidth;
int childSize = mOrientationHandler.getMeasuredSize(layout);
int halfChildSize = (childSize / 2);
int childCenter = getChildOffset(i) + halfChildSize;
int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1410,7 +1464,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
protected boolean isInOverScroll() {
return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX);
int scroll = mOrientationHandler.getPrimaryScroll(this);
return scroll > mMaxScroll || scroll < mMinScroll;
}
protected int getPageSnapDuration() {
@@ -1432,10 +1487,10 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = validateNewPage(whichPage);
int halfScreenSize = getMeasuredWidth() / 2;
int halfScreenSize = mOrientationHandler.getMeasuredSize(this) / 2;
final int newX = getScrollForPage(whichPage);
int delta = newX - getUnboundedScrollX();
final int newLoc = getScrollForPage(whichPage);
int delta = newLoc - getUnboundedScroll();
int duration = 0;
if (Math.abs(velocity) < mMinFlingVelocity) {
@@ -1462,7 +1517,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (QUICKSTEP_SPRINGS.get()) {
return snapToPage(whichPage, delta, duration, false, null,
velocity * Math.signum(newX - getUnboundedScrollX()), true);
velocity * Math.signum(delta), true);
} else {
return snapToPage(whichPage, delta, duration);
}
@@ -1488,8 +1543,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
TimeInterpolator interpolator) {
whichPage = validateNewPage(whichPage);
int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX();
int newLoc = getScrollForPage(whichPage);
final int delta = newLoc - getUnboundedScroll();
return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
}
@@ -1535,9 +1590,9 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
}
if (spring && QUICKSTEP_SPRINGS.get()) {
mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity);
mScroller.startScrollSpring(getUnboundedScroll(), delta, duration, velocity);
} else {
mScroller.startScroll(getUnboundedScrollX(), delta, duration);
mScroller.startScroll(getUnboundedScroll(), delta, duration);
}
updatePageIndicator();
+2 -2
View File
@@ -21,12 +21,12 @@ import android.util.DisplayMetrics;
import android.util.TypedValue;
public class ResourceUtils {
public static final int DEFAULT_NAVBAR_VALUE = 48;
public static final String NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE = "navigation_bar_width";
public static final String NAVBAR_BOTTOM_GESTURE_SIZE = "navigation_bar_gesture_height";
public static int getNavbarSize(String resName, Resources res) {
return getDimenByName(resName, res, 48);
return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
}
public static int getDimenByName(String resName, Resources res, int defaultValue) {
@@ -67,7 +67,6 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
super(context, attrs, defStyle);
mCacheExpireAlarm = new Alarm();
mCacheExpireAlarm.setOnAlarmListener(this);
}
@Override
@@ -75,6 +74,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
super.onAttachedToWindow();
if (mHadPendingAlarm) {
mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
mCacheExpireAlarm.setOnAlarmListener(this);
mHadPendingAlarm = false;
}
}
@@ -84,6 +84,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
super.onDetachedFromWindow();
if (mCacheExpireAlarm.alarmPending()) {
mCacheExpireAlarm.cancelAlarm();
mCacheExpireAlarm.setOnAlarmListener(null);
mHadPendingAlarm = true;
}
}
@@ -168,6 +169,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
}
// Cancel any pending alarm and set cache expiry after some time
mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
mCacheExpireAlarm.setOnAlarmListener(this);
if (uninstallDisabled) {
return false;
}
+6
View File
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
import static com.android.launcher3.states.RotationHelper.FIXED_ROTATION_TRANSFORM_SETTING_NAME;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
@@ -127,6 +128,11 @@ public final class Utilities {
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
}
public static boolean isForcedRotation(Context context) {
return Settings.Global.getInt(context.getContentResolver(),
FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
}
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
+3 -3
View File
@@ -966,8 +966,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
((mIsRtl && getUnboundedScrollX() > mMaxScrollX)
|| (!mIsRtl && getUnboundedScrollX() < mMinScrollX));
((mIsRtl && getUnboundedScroll() > mMaxScroll)
|| (!mIsRtl && getUnboundedScroll() < mMinScroll));
}
@Override
@@ -1003,7 +1003,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
public void showPageIndicatorAtCurrentScroll() {
if (mPageIndicator != null) {
mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
mPageIndicator.setScroll(getScrollX(), computeMaxScroll());
}
}
@@ -98,8 +98,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
protected boolean mUsingTabs;
private boolean mSearchModeWhileUsingTabs = false;
private RecyclerViewFastScroller mTouchHandler;
private final Point mFastScrollerOffset = new Point();
protected RecyclerViewFastScroller mTouchHandler;
protected final Point mFastScrollerOffset = new Point();
private final MultiValueAlpha mMultiValueAlpha;
@@ -238,6 +238,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
AllAppsRecyclerView rv = getActiveRecyclerView();
if (rv != null && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(),
mFastScrollerOffset)) {
mTouchHandler = rv.getScrollbar();
} else {
mTouchHandler = null;
}
}
if (mTouchHandler != null) {
mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
return true;
@@ -48,7 +48,7 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mPageIndicator.setScroll(l, mMaxScrollX);
mPageIndicator.setScroll(l, mMaxScroll);
}
@Override
@@ -32,8 +32,10 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.systemui.plugins.ResourceProvider;
/**
* Handles AllApps view transition.
@@ -47,9 +49,6 @@ import com.android.launcher3.views.ScrimView;
*/
public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
private static final float SPRING_DAMPING_RATIO = 0.75f;
private static final float SPRING_STIFFNESS = 600f;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -187,8 +186,12 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
public Animator createSpringAnimation(float... progressValues) {
if (UNSTABLE_SPRINGS.get()) {
ResourceProvider rp = DynamicResource.provider(mLauncher);
float damping = rp.getFloat(R.dimen.all_apps_spring_damping_ratio);
float stiffness = rp.getFloat(R.dimen.all_apps_spring_stiffness);
return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
damping, stiffness, progressValues);
}
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
}
@@ -51,7 +51,10 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
public boolean onInterceptTouchEvent(MotionEvent ev) {
// The AllAppsContainerView houses the QSB and is hence visible from the Workspace
// Overview states. We shouldn't intercept for the scrubber in these cases.
if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false;
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
mTouchHandler = null;
return false;
}
return super.onInterceptTouchEvent(ev);
}
@@ -15,32 +15,40 @@
*/
package com.android.launcher3.anim;
import android.content.Context;
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
import androidx.dynamicanimation.animation.FlingAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.R;
import com.android.launcher3.util.DynamicResource;
import com.android.systemui.plugins.ResourceProvider;
/**
* Given a property to animate and a target value and starting velocity, first apply friction to
* the fling until we pass the target, then apply a spring force to pull towards the target.
*/
public class FlingSpringAnim {
private static final float FLING_FRICTION = 1.5f;
private static final float SPRING_STIFFNESS = 200;
private static final float SPRING_DAMPING = 0.8f;
private final FlingAnimation mFlingAnim;
private SpringAnimation mSpringAnim;
private float mTargetPosition;
public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
float targetPosition, float startVelocity, float minVisChange, float minValue,
float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) {
public <K> FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property,
float startPosition, float targetPosition, float startVelocity, float minVisChange,
float minValue, float maxValue, float springVelocityFactor,
OnAnimationEndListener onEndListener) {
ResourceProvider rp = DynamicResource.provider(context);
float damping = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
float stiffness = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness);
float friction = rp.getFloat(R.dimen.swipe_up_rect_xy_fling_friction);
mFlingAnim = new FlingAnimation(object, property)
.setFriction(FLING_FRICTION)
.setFriction(friction)
// Have the spring pull towards the target if we've slowed down too much before
// reaching it.
.setMinimumVisibleChange(minVisChange)
@@ -54,8 +62,8 @@ public class FlingSpringAnim {
.setStartValue(value)
.setStartVelocity(velocity * springVelocityFactor)
.setSpring(new SpringForce(mTargetPosition)
.setStiffness(SPRING_STIFFNESS)
.setDampingRatio(SPRING_DAMPING));
.setStiffness(stiffness)
.setDampingRatio(damping));
mSpringAnim.addEndListener(onEndListener);
mSpringAnim.animateToFinalPosition(mTargetPosition);
}));
@@ -101,11 +101,15 @@ public final class FeatureFlags {
"Adds localized title and keyword search and ranking");
public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
"ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
"ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
"FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
"Use a launcher-provided OverscrollPlugin if available");
public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
@@ -113,6 +117,10 @@ public final class FeatureFlags {
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag(
"HOTSEAT_MIGRATE_NEW_PAGE", true,
"Migrates hotseat to a new workspace page instead of same page");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
@@ -120,7 +128,7 @@ public final class FeatureFlags {
"MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true,
"Show launcher preview in grid picker");
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
@@ -139,6 +147,11 @@ public final class FeatureFlags {
"ENABLE_LSQ_VELOCITY_PROVIDER", false,
"Use Least Square algorithm for motion pause detection.");
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
getDebugFlag(
"ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
"Always use hardware optimization for folder animations.");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -26,6 +26,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
@@ -546,6 +547,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
if (ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS.get()) return true;
int folderCount = 0;
final ShortcutAndWidgetContainer container = currentCellLayout.getShortcutsAndWidgets();
for (int i = container.getChildCount() - 1; i >= 0; --i) {
@@ -258,7 +258,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mPageIndicator.setScroll(l, mMaxScrollX);
mPageIndicator.setScroll(l, mMaxScroll);
}
/**
@@ -87,9 +87,11 @@ import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Arrays;
@@ -128,7 +130,8 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private static final Set<MainThreadInitializedObject> WHITELIST = new HashSet<>(
Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE));
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE));
private final InvariantDeviceProfile mIdp;
private final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
@@ -145,6 +148,14 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
return this;
}
public void onDestroy() {
CustomWidgetManager customWidgetManager = (CustomWidgetManager) mObjectMap.get(
CustomWidgetManager.INSTANCE);
if (customWidgetManager != null) {
customWidgetManager.onDestroy();
}
}
/**
* Find a cached object from mObjectMap if we have already created one. If not, generate
* an object using the provider.
@@ -349,6 +360,9 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
info.providerName);
if (widgetItem == null) {
return;
}
AppWidgetHostView view = new AppWidgetHostView(mContext);
view.setAppWidget(-1, widgetItem.widgetInfo);
view.updateAppWidget(null);
@@ -385,9 +399,11 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
}
WorkspaceFetcher fetcher;
PreviewContext previewContext = null;
if (needsToMigrate && success) {
previewContext = new PreviewContext(mContext, mIdp);
LauncherAppState appForPreview = new LauncherAppState(
new PreviewContext(mContext, mIdp), null /* iconCacheFileName */);
previewContext, null /* iconCacheFileName */);
fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
MODEL_EXECUTOR.execute(fetcher);
} else {
@@ -396,6 +412,9 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
(LauncherModel.ModelUpdateTask) fetcher);
}
WorkspaceResult workspaceResult = fetcher.get();
if (previewContext != null) {
previewContext.onDestroy();
}
if (workspaceResult == null) {
return;
@@ -557,6 +576,7 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
public WorkspaceResult call() throws Exception {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI);
mBgDataModel.widgetsModel.update(mApp, null);
return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
mBgDataModel.widgetsModel);
}
@@ -38,7 +38,7 @@ public final class FileLog {
private static final DateFormat DATE_FORMAT =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
private static final long MAX_LOG_FILE_SIZE = 4 << 20; // 4 mb
private static final long MAX_LOG_FILE_SIZE = 8 << 20; // 4 mb
private static Handler sHandler = null;
private static File sLogsDirectory = null;
@@ -104,7 +104,7 @@ import java.util.concurrent.CancellationException;
public class LoaderTask implements Runnable {
private static final String TAG = "LoaderTask";
private final LauncherAppState mApp;
protected final LauncherAppState mApp;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
@@ -0,0 +1,104 @@
/*
*
* * Copyright (C) 2020 The Android Open Source Project
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package com.android.launcher3.model;
import android.view.Surface;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.touch.LandscapePagedViewHandler;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.SeascapePagedViewHandler;
/**
* Container to hold orientation/rotation related information for Launcher.
* This is not meant to be an abstraction layer for applying different functionality between
* the different orientation/rotations. For that see {@link PagedOrientationHandler}
*
* This class has initial default state assuming the device and foreground app have
* no ({@link Surface.ROTATION_0} rotation.
*
* Currently this class resides in {@link com.android.launcher3.PagedView}, but there's a ticket
* to disassociate it from Launcher since it's needed before Launcher is instantiated
* See TODO(b/150300347)
*/
public final class PagedViewOrientedState {
private PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler();
private int mTouchRotation = Surface.ROTATION_0;
private int mDisplayRotation = Surface.ROTATION_0;
/**
* If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake
* launcher orientations.
*/
private boolean mDisableMultipleOrientations;
public void update(int touchRotation, int displayRotation) {
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
if (mTouchRotation == Surface.ROTATION_90) {
mOrientationHandler = new LandscapePagedViewHandler();
} else if (mTouchRotation == Surface.ROTATION_270) {
mOrientationHandler = new SeascapePagedViewHandler();
} else {
mOrientationHandler = new PortraitPagedViewHandler();
}
}
/**
* @return {@code true} if the area where the user touched the nav bar is the expected
* location for the given display rotation. Ex. bottom of phone in portrait, or left side of
* phone in landscape, right side in seascape, etc.
* False otherwise
*/
public boolean isTouchRegionNaturalForDisplay() {
return mTouchRotation == mDisplayRotation;
}
public boolean areMultipleLayoutOrientationsDisabled() {
return mDisableMultipleOrientations;
}
public void disableMultipleOrientations(boolean disable) {
mDisableMultipleOrientations = disable;
if (disable) {
mOrientationHandler = new PortraitPagedViewHandler();
}
}
public int getDisplayRotation() {
return mDisplayRotation;
}
/**
* Gets the difference between the rotation of the device/display and which region the
* user is currently interacting with in factors of 90 degree clockwise rotations.
* Ex. Display is in portrait -> 0, user touches landscape region -> 1, this
* method would return 3 because it takes 3 clockwise 90 degree rotations from normal to
* landscape (portrait -> seascape -> reverse portrait -> landscape)
*/
public int getTouchDisplayDelta() {
return RotationHelper.deltaRotation(mTouchRotation, mDisplayRotation);
}
public PagedOrientationHandler getOrientationHandler() {
return mOrientationHandler;
}
}
@@ -176,7 +176,7 @@ public class NotificationMainView extends FrameLayout implements SingleAxisSwipe
// SingleAxisSwipeDetector.Listener's
@Override
public void onDragStart(boolean start) { }
public void onDragStart(boolean start, float startDisplacement) { }
@Override
@@ -96,6 +96,7 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
initFlags();
loadPluginPrefs();
maybeAddSandboxCategory();
}
@Override
@@ -203,6 +204,31 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
});
}
private void maybeAddSandboxCategory() {
Context context = getContext();
if (context == null) {
return;
}
Intent launchSandboxIntent =
new Intent("com.android.quickstep.action.GESTURE_SANDBOX")
.setPackage(context.getPackageName())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (launchSandboxIntent.resolveActivity(context.getPackageManager()) == null) {
return;
}
PreferenceCategory sandboxCategory = newCategory("Sandbox");
Preference launchSandboxPreference = new Preference(context);
launchSandboxPreference.setKey("launchSandbox");
launchSandboxPreference.setTitle("Launch Gesture Navigation Sandbox");
launchSandboxPreference.setSummary(
"This provides tutorials and a place to practice navigation gestures.");
launchSandboxPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent);
return true;
});
sandboxCategory.addPreference(launchSandboxPreference);
}
private String toName(String action) {
String str = action.replace("com.android.systemui.action.PLUGIN_", "")
.replace("com.android.launcher3.action.PLUGIN_", "");
@@ -17,20 +17,35 @@ package com.android.launcher3.states;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.provider.Settings;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.WindowManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.UiThreadHelper;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class to manage launcher rotation
*/
@@ -38,15 +53,39 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
private final ContentResolver mContentResolver;
/**
* Listener to receive changes when {@link #FIXED_ROTATION_TRANSFORM_SETTING_NAME} flag changes.
*/
public interface ForcedRotationChangedListener {
void onForcedRotationChanged(boolean isForcedRotation);
}
public static boolean getAllowRotationDefaultValue() {
// If the device was scaled, used the original dimensions to determine if rotation
// is allowed of not.
// If the device's pixel density was scaled (usually via settings for A11y), use the
// original dimensions to determine if rotation is allowed of not.
Resources res = Resources.getSystem();
int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
* res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
return originalSmallestWidth >= 600;
}
private final ContentObserver mContentObserver =
new ContentObserver(MAIN_EXECUTOR.getHandler()) {
@Override
public void onChange(boolean selfChange) {
boolean forcedRotation = Utilities.isForcedRotation(mLauncher);
PagedView.sFlagForcedRotation = forcedRotation;
updateForcedRotation();
for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) {
listener.onForcedRotationChanged(forcedRotation);
}
}
};
public static final int REQUEST_NONE = 0;
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
@@ -56,6 +95,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
private boolean mIgnoreAutoRotateSettings;
private boolean mAutoRotateEnabled;
private boolean mForcedRotation;
private List<ForcedRotationChangedListener> mForcedRotationChangedListeners = new ArrayList<>();
/**
* Rotation request made by
@@ -84,6 +125,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
// On large devices we do not handle auto-rotate differently.
mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
updateForcedRotation();
if (!mIgnoreAutoRotateSettings) {
mPrefs = Utilities.getPrefs(mLauncher);
mPrefs.registerOnSharedPreferenceChangeListener(this);
@@ -92,6 +134,26 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
} else {
mPrefs = null;
}
// TODO(b/150260456) Add this in home settings as well
mContentResolver = launcher.getContentResolver();
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
FIXED_ROTATION_TRANSFORM_SETTING_NAME), false, mContentObserver);
}
private void updateForcedRotation() {
mForcedRotation = !getAllowRotationDefaultValue() && Utilities.isForcedRotation(mLauncher);
}
/**
* will not be called when first registering the listener.
*/
public void addForcedRotationCallback(ForcedRotationChangedListener listener) {
mForcedRotationChangedListeners.add(listener);
}
public void removeForcedRotationCallback(ForcedRotationChangedListener listener) {
mForcedRotationChangedListeners.remove(listener);
}
public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) {
@@ -173,6 +235,10 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
if (mPrefs != null) {
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
if (mContentResolver != null) {
mContentResolver.unregisterContentObserver(mContentObserver);
}
mForcedRotationChangedListeners.clear();
}
}
@@ -182,7 +248,10 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
}
final int activityFlags;
if (mStateHandlerRequest != REQUEST_NONE) {
if (mForcedRotation) {
// TODO(b/150214193) Properly address this
activityFlags = SCREEN_ORIENTATION_PORTRAIT;
} else if (mStateHandlerRequest != REQUEST_NONE) {
activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
} else if (mCurrentTransitionRequest != REQUEST_NONE) {
@@ -204,6 +273,118 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
}
}
public static int getDegreesFromRotation(int rotation) {
int degrees;
switch (rotation) {
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
case Surface.ROTATION_0:
default:
degrees = 0;
break;
}
return degrees;
}
public static int getRotationFromDegrees(int degrees) {
int threshold = 70;
if (degrees >= (360 - threshold) || degrees < (threshold)) {
return Surface.ROTATION_0;
} else if (degrees < (90 + threshold)) {
return Surface.ROTATION_270;
} else if (degrees < 180 + threshold) {
return Surface.ROTATION_180;
} else {
return Surface.ROTATION_90;
}
}
/**
* @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
* E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
* A value of 0 means no rotation has been applied
*/
public static int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
return delta;
}
/**
* Creates a matrix to transform the given motion event specified by degrees.
* If {@param inverse} is {@code true}, the inverse of that matrix will be applied
*/
public static void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
Matrix transform = new Matrix();
transform.setRotate(degrees);
if (inverse) {
Matrix inv = new Matrix();
transform.invert(inv);
ev.transform(inv);
} else {
ev.transform(transform);
}
// TODO: Add scaling back in based on degrees
// if (getWidth() > 0 && getHeight() > 0) {
// float scale = ((float) getWidth()) / getHeight();
// transform.postScale(scale, 1 / scale);
// }
}
/**
* TODO(b/149658423): Have {@link com.android.quickstep.OrientationTouchTransformer
* also use this}
*/
public static Matrix getRotationMatrix(int screenWidth, int screenHeight, int displayRotation) {
Matrix m = new Matrix();
switch (displayRotation) {
case Surface.ROTATION_0:
return m;
case Surface.ROTATION_90:
m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation));
m.postTranslate(0, screenWidth);
break;
case Surface.ROTATION_270:
m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation));
m.postTranslate(screenHeight, 0);
break;
}
return m;
}
public static void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight,
int displayRotation) {
Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
m.mapRect(src);
}
public static void mapInverseRectFromNormalOrientation(RectF src, int screenWidth,
int screenHeight, int displayRotation) {
Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
Matrix inverse = new Matrix();
m.invert(inverse);
inverse.mapRect(src);
}
public static void getTargetRectForRotation(Rect srcOut, int screenWidth, int screenHeight,
int displayRotation) {
RectF wrapped = new RectF(srcOut);
Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
m.mapRect(wrapped);
wrapped.round(srcOut);
}
public static boolean isRotationLandscape(int rotation) {
return rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90;
}
@Override
public String toString() {
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
@@ -230,7 +230,7 @@ public abstract class AbstractStateChangeTouchController
}
@Override
public void onDragStart(boolean start) {
public void onDragStart(boolean start, float startDisplacement) {
mStartState = mLauncher.getStateManager().getState();
mIsLogContainerSet = false;
if (mCurrentAnimation == null) {
@@ -236,7 +236,7 @@ public abstract class BaseSwipeDetector {
} else {
mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
}
}
}
protected abstract boolean shouldScrollStart(PointF displacement);
@@ -0,0 +1,253 @@
/*
* 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.touch;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.OverScroller;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
public class LandscapePagedViewHandler implements PagedOrientationHandler {
@Override
public int getPrimaryValue(int x, int y) {
return y;
}
@Override
public int getSecondaryValue(int x, int y) {
return x;
}
@Override
public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int minMaxScroll) {
pagedView.superScrollTo(secondaryScroll, minMaxScroll);
}
@Override
public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) {
pagedView.scrollTo(pagedView.getScrollX() + x, unboundedScroll + y);
}
@Override
public void scrollerStartScroll(OverScroller scroller, int newPosition) {
scroller.startScroll(scroller.getCurrPos(), newPosition - scroller.getCurrPos());
}
@Override
public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) {
int scroll = pagedView.getScrollY();
final int halfPageSize = pagedView.getNormalChildHeight() / 2;
final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize;
final int halfScreenSize = pagedView.getMeasuredHeight() / 2;
return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize);
}
@Override
public boolean isGoingUp(float displacement) {
return displacement > 0;
}
@Override
public void adjustFloatingIconStartVelocity(PointF velocity) {
float oldX = velocity.x;
float oldY = velocity.y;
velocity.set(-oldY, oldX);
}
@Override
public void delegateScrollTo(PagedView pagedView, int primaryScroll) {
pagedView.superScrollTo(pagedView.getScrollX(), primaryScroll);
}
@Override
public <T> void set(T target, Int2DAction<T> action, int param) {
action.call(target, 0, param);
}
@Override
public <T> void set(T target, Float2DAction<T> action, float param) {
action.call(target, 0, param);
}
@Override
public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
return event.getY(pointerIndex);
}
@Override
public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) {
return velocityTracker.getYVelocity(pointerId);
}
@Override
public int getMeasuredSize(View view) {
return view.getMeasuredHeight();
}
@Override
public int getPrimarySize(Rect rect) {
return rect.height();
}
@Override
public float getPrimarySize(RectF rect) {
return rect.height();
}
@Override
public int getSecondaryDimension(View view) {
return view.getWidth();
}
@Override
public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) {
float offscreenTranslationY = dp.heightPx - view.getPaddingTop();
return new ScaleAndTranslation(1f, 0f, offscreenTranslationY);
}
@Override
public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) {
return scaleAndTranslation.translationY;
}
@Override
public FloatProperty<View> getPrimaryViewTranslate() {
return VIEW_TRANSLATE_Y;
}
@Override
public FloatProperty<View> getSecondaryViewTranslate() {
return VIEW_TRANSLATE_X;
}
@Override
public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
view.setTranslationX(0);
view.setTranslationY(translation);
}
@Override
public float getViewCenterPosition(View view) {
return view.getTop() + view.getTranslationY();
}
@Override
public int getPrimaryScroll(View view) {
return view.getScrollY();
}
@Override
public float getPrimaryScale(View view) {
return view.getScaleY();
}
@Override
public void setMaxScroll(AccessibilityEvent event, int maxScroll) {
event.setMaxScrollY(maxScroll);
}
@Override
public boolean getRecentsRtlSetting(Resources resources) {
return !Utilities.isRtl(resources);
}
@Override
public float getDegreesRotated() {
return 90;
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(0, -value);
} else {
rect.offset(-value, 0);
}
}
@Override
public int getChildStart(View view) {
return view.getTop();
}
@Override
public int getCenterForPage(View view, Rect insets) {
return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left
- insets.right - view.getPaddingRight()) / 2;
}
@Override
public int getScrollOffsetStart(View view, Rect insets) {
return insets.top + view.getPaddingTop();
}
@Override
public int getScrollOffsetEnd(View view, Rect insets) {
return view.getHeight() - view.getPaddingBottom() - insets.bottom;
}
@Override
public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
return HORIZONTAL;
}
@Override
public int getShortEdgeLength(DeviceProfile dp) {
return dp.heightPx;
}
@Override
public int getTaskDismissDirectionFactor() {
return 1;
}
@Override
public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
boolean layoutChild) {
final int childHeight = child.getMeasuredHeight();
final int childBottom = childStart + childHeight;
final int childWidth = child.getMeasuredWidth();
final int childLeft = pageCenter - childWidth/ 2;
if (layoutChild) {
child.layout(childLeft, childStart, childLeft + childWidth, childBottom);
}
return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
}
}
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.touch;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.util.OverScroller;
/**
* Abstraction layer to separate horizontal and vertical specific implementations
* for {@link com.android.launcher3.PagedView}. Majority of these implementations are (should be) as
* simple as choosing the correct X and Y analogous methods.
*/
public interface PagedOrientationHandler {
interface Int2DAction<T> {
void call(T target, int x, int y);
}
interface Float2DAction<T> {
void call(T target, float x, float y);
}
Int2DAction<View> VIEW_SCROLL_BY = View::scrollBy;
Int2DAction<View> VIEW_SCROLL_TO = View::scrollTo;
Float2DAction<Canvas> CANVAS_TRANSLATE = Canvas::translate;
<T> void set(T target, Int2DAction<T> action, int param);
<T> void set(T target, Float2DAction<T> action, float param);
float getPrimaryDirection(MotionEvent event, int pointerIndex);
float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
int getMeasuredSize(View view);
int getPrimarySize(Rect rect);
float getPrimarySize(RectF rect);
int getSecondaryDimension(View view);
LauncherState.ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view);
float getTranslationValue(LauncherState.ScaleAndTranslation scaleAndTranslation);
FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> getSecondaryViewTranslate();
void setPrimaryAndResetSecondaryTranslate(View view, float translation);
float getViewCenterPosition(View view);
int getPrimaryScroll(View view);
float getPrimaryScale(View view);
int getChildStart(View view);
int getCenterForPage(View view, Rect insets);
int getScrollOffsetStart(View view, Rect insets);
int getScrollOffsetEnd(View view, Rect insets);
SingleAxisSwipeDetector.Direction getOppositeSwipeDirection();
int getShortEdgeLength(DeviceProfile dp);
int getTaskDismissDirectionFactor();
ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
void setMaxScroll(AccessibilityEvent event, int maxScroll);
boolean getRecentsRtlSetting(Resources resources);
float getDegreesRotated();
void offsetTaskRect(RectF rect, float value, int delta);
int getPrimaryValue(int x, int y);
int getSecondaryValue(int x, int y);
void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
/** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/
void delegateScrollTo(PagedView pagedView, int primaryScroll);
void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
void scrollerStartScroll(OverScroller scroller, int newPosition);
CurveProperties getCurveProperties(PagedView pagedView, Rect insets);
boolean isGoingUp(float displacement);
/**
* Maps the velocity from the coordinate plane of the foreground app to that
* of Launcher's (which now will always be portrait)
*/
void adjustFloatingIconStartVelocity(PointF velocity);
class CurveProperties {
public final int scroll;
public final int halfPageSize;
public final int screenCenter;
public final int halfScreenSize;
public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) {
this.scroll = scroll;
this.halfPageSize = halfPageSize;
this.screenCenter = screenCenter;
this.halfScreenSize = halfScreenSize;
}
}
class ChildBounds {
public final int primaryDimension;
public final int secondaryDimension;
public final int childPrimaryEnd;
public final int childSecondaryEnd;
ChildBounds(int primaryDimension, int secondaryDimension, int childPrimaryEnd,
int childSecondaryEnd) {
this.primaryDimension = primaryDimension;
this.secondaryDimension = secondaryDimension;
this.childPrimaryEnd = childPrimaryEnd;
this.childSecondaryEnd = childSecondaryEnd;
}
}
}
@@ -0,0 +1,251 @@
/*
* 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.touch;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.OverScroller;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
public class PortraitPagedViewHandler implements PagedOrientationHandler {
@Override
public int getPrimaryValue(int x, int y) {
return x;
}
@Override
public int getSecondaryValue(int x, int y) {
return y;
}
@Override
public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll) {
pagedView.superScrollTo(primaryScroll, secondaryScroll);
}
@Override
public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) {
pagedView.scrollTo(unboundedScroll + x, pagedView.getScrollY() + y);
}
@Override
public void scrollerStartScroll(OverScroller scroller, int newPosition) {
scroller.startScroll(newPosition - scroller.getCurrPos(), scroller.getCurrPos());
}
@Override
public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) {
int scroll = pagedView.getScrollX();
final int halfPageSize = pagedView.getNormalChildWidth() / 2;
final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize;
final int halfScreenSize = pagedView.getMeasuredWidth() / 2;
return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize);
}
@Override
public boolean isGoingUp(float displacement) {
return displacement < 0;
}
@Override
public void adjustFloatingIconStartVelocity(PointF velocity) {
//no-op
}
@Override
public void delegateScrollTo(PagedView pagedView, int primaryScroll) {
pagedView.superScrollTo(primaryScroll, pagedView.getScrollY());
}
@Override
public <T> void set(T target, Int2DAction<T> action, int param) {
action.call(target, param, 0);
}
@Override
public <T> void set(T target, Float2DAction<T> action, float param) {
action.call(target, param, 0);
}
@Override
public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
return event.getX(pointerIndex);
}
@Override
public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) {
return velocityTracker.getXVelocity(pointerId);
}
@Override
public int getMeasuredSize(View view) {
return view.getMeasuredWidth();
}
@Override
public int getPrimarySize(Rect rect) {
return rect.width();
}
@Override
public float getPrimarySize(RectF rect) {
return rect.width();
}
@Override
public int getSecondaryDimension(View view) {
return view.getHeight();
}
@Override
public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) {
float offscreenTranslationX = dp.widthPx - view.getPaddingStart();
return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
}
@Override
public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) {
return scaleAndTranslation.translationX;
}
@Override
public FloatProperty<View> getPrimaryViewTranslate() {
return VIEW_TRANSLATE_X;
}
@Override
public FloatProperty<View> getSecondaryViewTranslate() {
return VIEW_TRANSLATE_Y;
}
@Override
public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
view.setTranslationX(translation);
view.setTranslationY(0);
}
@Override
public float getViewCenterPosition(View view) {
return view.getLeft() + view.getTranslationX();
}
@Override
public int getPrimaryScroll(View view) {
return view.getScrollX();
}
@Override
public float getPrimaryScale(View view) {
return view.getScaleX();
}
@Override
public void setMaxScroll(AccessibilityEvent event, int maxScroll) {
event.setMaxScrollX(maxScroll);
}
@Override
public boolean getRecentsRtlSetting(Resources resources) {
return !Utilities.isRtl(resources);
}
@Override
public float getDegreesRotated() {
return 0;
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(0, -value);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(-value, 0);
} else {
rect.offset(0, value);
}
}
@Override
public int getChildStart(View view) {
return view.getLeft();
}
@Override
public int getCenterForPage(View view, Rect insets) {
return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top
- insets.bottom - view.getPaddingBottom()) / 2;
}
@Override
public int getScrollOffsetStart(View view, Rect insets) {
return insets.left + view.getPaddingLeft();
}
@Override
public int getScrollOffsetEnd(View view, Rect insets) {
return view.getWidth() - view.getPaddingRight() - insets.right;
}
@Override
public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
return VERTICAL;
}
@Override
public int getShortEdgeLength(DeviceProfile dp) {
return dp.widthPx;
}
@Override
public int getTaskDismissDirectionFactor() {
return -1;
}
@Override
public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
boolean layoutChild) {
final int childWidth = child.getMeasuredWidth();
final int childRight = childStart + childWidth;
final int childHeight = child.getMeasuredHeight();
final int childTop = pageCenter - childHeight / 2;
if (layoutChild) {
child.layout(childStart, childTop, childRight, childTop + childHeight);
}
return new ChildBounds(childWidth, childHeight, childRight, childTop);
}
}
@@ -0,0 +1,67 @@
/*
* 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.touch;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.RectF;
import android.view.Surface;
import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@Override
public int getTaskDismissDirectionFactor() {
return -1;
}
@Override
public boolean getRecentsRtlSetting(Resources resources) {
return Utilities.isRtl(resources);
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(0, -value);
} else {
rect.offset(-value, 0);
}
}
@Override
public float getDegreesRotated() {
return 270;
}
@Override
public boolean isGoingUp(float displacement) {
return displacement < 0;
}
@Override
public void adjustFloatingIconStartVelocity(PointF velocity) {
float oldX = velocity.x;
float oldY = velocity.y;
velocity.set(oldY, -oldX);
}
}
@@ -148,7 +148,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
@Override
protected void reportDragStartInternal(boolean recatch) {
mListener.onDragStart(!recatch);
float startDisplacement = mDir.extractDirection(mSubtractDisplacement);
mListener.onDragStart(!recatch, startDisplacement);
}
@Override
@@ -165,8 +166,13 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
/** Listener to receive updates on the swipe. */
public interface Listener {
/** @param start whether this was the original drag start, as opposed to a recatch. */
void onDragStart(boolean start);
/**
* TODO(b/150256055) consolidate all the different onDrag() methods into one
* @param start whether this was the original drag start, as opposed to a recatch.
* @param startDisplacement the initial touch displacement for the primary direction as
* given by by {@link Direction#extractDirection(PointF)}
*/
void onDragStart(boolean start, float startDisplacement);
boolean onDrag(float displacement);
@@ -28,6 +28,8 @@ import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
/**
@@ -127,6 +129,18 @@ public class DefaultDisplay implements DisplayListener {
public final DisplayMetrics metrics;
@VisibleForTesting
public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize,
Point largestSize, DisplayMetrics metrics) {
this.id = id;
this.rotation = rotation;
this.singleFrameMs = singleFrameMs;
this.realSize = realSize;
this.smallestSize = smallestSize;
this.largestSize = largestSize;
this.metrics = metrics;
}
private Info(Context context) {
this(context.getSystemService(WindowManager.class).getDefaultDisplay());
}
@@ -68,6 +68,11 @@ public class DynamicResource implements ResourceProvider, PluginListener<Resourc
return mContext.getResources().getColor(resId, null);
}
@Override
public float getFloat(@DimenRes int resId) {
return mContext.getResources().getFloat(resId);
}
@Override
public void onPluginConnected(ResourceProvider plugin, Context context) {
mPlugin = plugin;
@@ -26,11 +26,13 @@ import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.R;
import com.android.systemui.plugins.ResourceProvider;
/**
* Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more
* customization options.
@@ -425,6 +427,7 @@ public class OverScroller {
// Current state of the animation.
private int mState = SPLINE;
private Context mContext;
private SpringAnimation mSpring;
// Constant gravity value, used in the deceleration phase.
@@ -500,6 +503,7 @@ public class OverScroller {
}
SplineOverScroller(Context context) {
mContext = context;
mFinished = true;
final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2)
@@ -560,9 +564,12 @@ public class OverScroller {
}
mSpring = new SpringAnimation(this, SPRING_PROPERTY);
ResourceProvider rp = DynamicResource.provider(mContext);
float stiffness = rp.getFloat(R.dimen.horizontal_spring_stiffness);
float damping = rp.getFloat(R.dimen.horizontal_spring_damping_ratio);
mSpring.setSpring(new SpringForce(mFinal)
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
.setStiffness(stiffness)
.setDampingRatio(damping));
mSpring.setStartVelocity(velocity);
mSpring.animateToFinalPosition(mFinal);
mSpring.addEndListener((animation, canceled, value, velocity1) -> {
@@ -149,8 +149,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
/* SingleAxisSwipeDetector.Listener */
@Override
public void onDragStart(boolean start) {
}
public void onDragStart(boolean start, float startDisplacement) { }
@Override
public boolean onDrag(float displacement) {
@@ -33,6 +33,7 @@ import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RemoteViews;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastBitmapDrawable;
@@ -93,6 +94,15 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
}
}
@Override
public void updateAppWidget(RemoteViews remoteViews) {
super.updateAppWidget(remoteViews);
WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext());
if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) {
reInflate();
}
}
@Override
public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
int maxHeight) {
@@ -49,6 +49,9 @@ import java.util.stream.Stream;
*/
public class WidgetManagerHelper {
//TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276
public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
final AppWidgetManager mAppWidgetManager;
final Context mContext;
@@ -127,6 +130,14 @@ public class WidgetManagerHelper {
return null;
}
/**
* Returns if a AppWidgetProvider has marked a widget restored
*/
public boolean isAppWidgetRestored(int appWidgetId) {
return !WidgetsModel.GO_DISABLE_WIDGETS && mAppWidgetManager.getAppWidgetOptions(
appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
}
public static Map<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap(Context context) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
return Collections.emptyMap();
@@ -51,6 +51,7 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
new MainThreadInitializedObject<>(CustomWidgetManager::new);
private final Context mContext;
/**
* auto provider Id is an ever-increasing number that serves as the providerId whenever a new
* custom widget has been connected.
@@ -62,6 +63,7 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
private Consumer<PackageUserKey> mWidgetRefreshCallback;
private CustomWidgetManager(Context context) {
mContext = context;
mPlugins = new SparseArray<>();
mCustomWidgets = new ArrayList<>();
mWidgetsIdMap = new SparseArray<>();
@@ -69,6 +71,10 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
.addPluginListener(this, CustomWidgetPlugin.class, true);
}
public void onDestroy() {
PluginManagerWrapper.INSTANCE.get(mContext).removePluginListener(this);
}
@Override
public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
mPlugins.put(mAutoProviderId, plugin);
@@ -44,4 +44,9 @@ public interface ResourceProvider extends Plugin {
* @see android.content.res.Resources#getColor(int)
*/
int getColor(int resId);
/**
* @see android.content.res.Resources#getFloat(int)
*/
float getFloat(int resId);
}
@@ -249,6 +249,10 @@ public class WidgetsModel {
ComponentName providerName) {
ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
new PackageItemInfo(providerName.getPackageName()));
if (widgetsList == null) {
return null;
}
for (WidgetItem item : widgetsList) {
if (item.componentName.equals(providerName)) {
return item;
@@ -89,7 +89,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 - mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -99,7 +99,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -107,7 +107,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener, never()).onDragStart(anyBoolean());
verify(mMockListener, never()).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -118,7 +118,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -129,7 +129,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -140,7 +140,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -151,7 +151,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDragStart(anyBoolean());
verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
}
@Test
@@ -34,10 +34,13 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Debug;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
@@ -68,6 +71,7 @@ import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.RuleChain;
@@ -117,6 +121,14 @@ public abstract class AbstractLauncherUiTest {
// so let's just mark the fact that the leak has happened.
if (sDetectedActivityLeak == null) {
sDetectedActivityLeak = violation.toString();
try {
Debug.dumpHprofData(
getInstrumentation().getTargetContext()
.getFilesDir().getPath()
+ "/ActivityLeakHeapDump.hprof");
} catch (IOException e) {
Log.e(TAG, "dumpHprofData failed", e);
}
}
});
StrictMode.setVmPolicy(builder.build());
@@ -126,18 +138,6 @@ public abstract class AbstractLauncherUiTest {
public static void checkDetectedLeaks() {
if (sDetectedActivityLeak != null && !sActivityLeakReported) {
sActivityLeakReported = true;
final UiDevice device = UiDevice.getInstance(getInstrumentation());
try {
device.executeShellCommand(
"am dumpheap "
+ device.getLauncherPackageName()
+ " "
+ getInstrumentation().getTargetContext().getFilesDir().getPath()
+ "/ActivityLeakHeapDump.hprof");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -215,6 +215,21 @@ public abstract class AbstractLauncherUiTest {
@Before
public void setUp() throws Exception {
final String launcherPackageName = mDevice.getLauncherPackageName();
try {
final Context context = InstrumentationRegistry.getContext();
final PackageManager pm = context.getPackageManager();
final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0);
if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) {
Assert.assertEquals("Launcher version doesn't match tests version",
pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(),
launcherPackage.getLongVersionCode());
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
mLauncherPid = 0;
// Disable app tracker
AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
@@ -21,6 +21,7 @@ import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOption
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.appwidget.AppWidgetHost;
@@ -33,6 +34,7 @@ import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.RemoteViews;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -41,6 +43,7 @@ import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -86,7 +89,8 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
// Clear all existing data
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
LauncherSettings.Settings.call(mResolver,
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
}
@After
@@ -172,6 +176,26 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
assertNotNull(AppWidgetManager.getInstance(mTargetContext)
.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
LauncherSettings.Favorites.APPWIDGET_ID))));
// send OPTION_APPWIDGET_RESTORE_COMPLETED
int appWidgetId = mCursor.getInt(
mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID));
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext);
Bundle b = new Bundle();
b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true);
RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready);
appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
// verify changes are reflected
waitForLauncherCondition("App widget options did not update",
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
executeOnLauncher(l -> l.getAppWidgetHost().startListening());
verifyWidgetPresent(info);
assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
}
@Test
@@ -254,6 +278,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
/**
* Creates a LauncherAppWidgetInfo corresponding to {@param info}
*
* @param bindWidget if true the info is bound and a valid widgetId is assigned to
* the LauncherAppWidgetInfo
*/
@@ -306,7 +331,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
.keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
return packages;
});
while(true) {
while (true) {
try {
mTargetContext.getPackageManager().getPackageInfo(
pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
@@ -316,7 +341,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
}
}
pkg = invalidPackage + count;
count ++;
count++;
}
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
new ComponentName(pkg, "com.test.widgetprovider"));
@@ -64,6 +64,7 @@ public class SimpleActivityRule<T extends Activity> implements TestRule {
mBase.evaluate();
} finally {
app.unregisterActivityLifecycleCallbacks(this);
mActivity = null;
}
}

Some files were not shown because too many files have changed in this diff Show More