diff --git a/proguard.flags b/proguard.flags index cac8930d5d..086337dfa2 100644 --- a/proguard.flags +++ b/proguard.flags @@ -39,7 +39,7 @@ public int getY(); } --keep class com.android.launcher3.dragndrop.DragLayer$LayoutParams { +-keep class com.android.launcher3.views.BaseDragLayer$LayoutParams { public void setWidth(int); public int getWidth(); public void setHeight(int); diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index d531a463b0..8b28597dd1 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -47,7 +47,21 @@ + android:exported="true" + android:excludeFromRecents="true" /> + + + + + + + diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index 9006831b02..e414fa033b 100644 Binary files a/quickstep/libs/sysui_shared.jar and b/quickstep/libs/sysui_shared.jar differ diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png deleted file mode 100644 index d4f995dfb1..0000000000 Binary files a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png and /dev/null differ diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png deleted file mode 100644 index 9013c1a2fb..0000000000 Binary files a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png and /dev/null differ diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png deleted file mode 100644 index 6e924ecbd9..0000000000 Binary files a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png and /dev/null differ diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png deleted file mode 100644 index 33d0edd787..0000000000 Binary files a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png and /dev/null differ diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png deleted file mode 100644 index 20c85e6711..0000000000 Binary files a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png and /dev/null differ diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png deleted file mode 100644 index 58a1ca07bf..0000000000 Binary files a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png and /dev/null differ diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png deleted file mode 100644 index 9d3dc318b9..0000000000 Binary files a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png and /dev/null differ diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png deleted file mode 100644 index 7fb248b9fa..0000000000 Binary files a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png and /dev/null differ diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png deleted file mode 100644 index 49ec7aa61c..0000000000 Binary files a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png and /dev/null differ diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png deleted file mode 100644 index a8922e2381..0000000000 Binary files a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png and /dev/null differ diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index c41684455b..b3fc4ed58c 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -15,15 +15,17 @@ --> + android:clipToPadding="false" + android:theme="@style/HomeScreenElementTheme" /> \ No newline at end of file diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 54a90cfe84..89e057148f 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -21,7 +21,7 @@ android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - android:alpha="0.0" - android:visibility="invisible" > + android:visibility="invisible" + android:focusableInTouchMode="true" > \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 849719191c..b751e0d9c9 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -22,7 +22,6 @@ 2dp 20dp 10dp - 100dp @@ -34,7 +33,7 @@ 25dp 80dp - 120dp + 140dp 80dp 150% diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml new file mode 100644 index 0000000000..2655402015 --- /dev/null +++ b/quickstep/res/xml/indexable_launcher_prefs.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index 872e6ca020..f91933979e 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -15,27 +15,92 @@ */ package com.android.launcher3; +import static com.android.systemui.shared.recents.utilities.Utilities + .postAtFrontOfQueueAsynchronously; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Handler; +import android.support.annotation.BinderThread; +import android.support.annotation.UiThread; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously; +@TargetApi(Build.VERSION_CODES.P) +public abstract class LauncherAnimationRunner extends AnimatorListenerAdapter + implements RemoteAnimationRunnerCompat { -public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { + private static final int REFRESH_RATE_MS = 16; - AnimatorSet mAnimator; - private Launcher mLauncher; + private final Handler mHandler; - LauncherAnimationRunner(Launcher launcher) { - mLauncher = launcher; + private Runnable mSysFinishRunnable; + + private AnimatorSet mAnimator; + + public LauncherAnimationRunner(Handler handler) { + mHandler = handler; } + @BinderThread + @Override + public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) { + postAtFrontOfQueueAsynchronously(mHandler, () -> { + // Finish any previous animation + finishSystemAnimation(); + + mSysFinishRunnable = runnable; + mAnimator = getAnimator(targetCompats); + if (mAnimator == null) { + finishSystemAnimation(); + return; + } + mAnimator.addListener(this); + mAnimator.start(); + // Because t=0 has the app icon in its original spot, we can skip the + // first frame and have the same movement one frame earlier. + mAnimator.setCurrentPlayTime(REFRESH_RATE_MS); + + }); + } + + + @UiThread + public abstract AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats); + + @UiThread + @Override + public void onAnimationEnd(Animator animation) { + if (animation == mAnimator) { + mAnimator = null; + finishSystemAnimation(); + } + } + + /** + * Called by the system + */ + @BinderThread @Override public void onAnimationCancelled() { - postAtFrontOfQueueAsynchronously(mLauncher.getWindow().getDecorView().getHandler(), () -> { + postAtFrontOfQueueAsynchronously(mHandler, () -> { if (mAnimator != null) { - mAnimator.cancel(); + mAnimator.removeListener(this); + mAnimator.end(); + mAnimator = null; } }); } + + @UiThread + private void finishSystemAnimation() { + if (mSysFinishRunnable != null) { + mSysFinishRunnable.run(); + mSysFinishRunnable = null; + } + } } \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index ba0cbfa77d..5a090d9e4c 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber; import static com.android.systemui.shared.recents.utilities.Utilities.getSurface; -import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; @@ -41,6 +40,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.Surface; import android.view.View; @@ -80,7 +80,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag implements OnDeviceProfileChangeListener { private static final String TAG = "LauncherTransition"; - private static final int REFRESH_RATE_MS = 16; private static final int STATUS_BAR_TRANSITION_DURATION = 120; private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = @@ -99,7 +98,9 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag private final DragLayer mDragLayer; private final Launcher mLauncher; - private DeviceProfile mDeviceProfile; + + private final Handler mHandler; + private final boolean mIsRtl; private final float mContentTransY; private final float mWorkspaceTransY; @@ -107,17 +108,23 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag private final float mRecentsTransY; private final float mRecentsScale; + private DeviceProfile mDeviceProfile; private View mFloatingView; - private boolean mIsRtl; - private LauncherTransitionAnimator mCurrentAnimator; + private final AnimatorListenerAdapter mReapplyStateListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLauncher.getStateManager().reapplyState(); + } + }; public LauncherAppTransitionManagerImpl(Context context) { mLauncher = Launcher.getLauncher(context); mDragLayer = mLauncher.getDragLayer(); + mHandler = new Handler(Looper.getMainLooper()); + mIsRtl = Utilities.isRtl(mLauncher.getResources()); mDeviceProfile = mLauncher.getDeviceProfile(); - mIsRtl = Utilities.isRtl(mLauncher.getResources()); Resources res = mLauncher.getResources(); mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y); @@ -135,26 +142,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag mDeviceProfile = dp; } - private void setCurrentAnimator(LauncherTransitionAnimator animator) { - if (isAnimating()) { - mCurrentAnimator.cancel(); - } - mCurrentAnimator = animator; - } - - @Override - public void finishLauncherAnimation() { - if (isAnimating()) { - mCurrentAnimator.finishLauncherAnimation(); - } - mCurrentAnimator = null; - } - - @Override - public boolean isAnimating() { - return mCurrentAnimator != null && mCurrentAnimator.isRunning(); - } - /** * @return ActivityOptions with remote animations that controls how the window of the opening * targets are displayed. @@ -162,58 +149,26 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag @Override public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) { if (hasControlRemoteAppTransitionPermission()) { - TaskView taskView = findTaskViewToLaunch(launcher, v); try { - RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) { + RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler) { + @Override - public void onAnimationStart(RemoteAnimationTargetCompat[] targets, - Runnable finishedCallback) { - // Post at front of queue ignoring sync barriers to make sure it gets - // processed before the next frame. - postAtFrontOfQueueAsynchronously(v.getHandler(), () -> { - final boolean removeTrackingView; - LauncherTransitionAnimator animator = composeRecentsLaunchAnimator( - taskView == null ? v : taskView, targets); - if (animator != null) { - // We are animating the task view directly, do not remove it after - removeTrackingView = false; - } else { - animator = composeAppLaunchAnimator(v, targets); - // A new floating view is created for the animation, remove it after - removeTrackingView = true; - } - - setCurrentAnimator(animator); - mAnimator = animator.getAnimatorSet(); - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Reset launcher to normal state - v.setVisibility(View.VISIBLE); - if (removeTrackingView) { - ((ViewGroup) mDragLayer.getParent()).removeView( - mFloatingView); - } - - mDragLayer.setAlpha(1f); - mDragLayer.setTranslationY(0f); - - View appsView = mLauncher.getAppsView(); - appsView.setAlpha(1f); - appsView.setTranslationY(0f); - - finishedCallback.run(); - } - }); - mAnimator.start(); - // Because t=0 has the app icon in its original spot, we can skip the - // first frame and have the same movement one frame earlier. - mAnimator.setCurrentPlayTime(REFRESH_RATE_MS); - }); + public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) { + Animator[] anims = composeRecentsLaunchAnimator(v, targetCompats); + AnimatorSet anim = new AnimatorSet(); + if (anims != null) { + anim.playTogether(anims); + } else { + anim.play(getLauncherAnimators(v, targetCompats)); + anim.play(getWindowAnimators(v, targetCompats)); + } + mLauncher.getStateManager().setCurrentAnimation(anim); + return anim; } }; - int duration = taskView != null ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION; + int duration = findTaskViewToLaunch(launcher, v, null) != null + ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION; int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION; return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( runner, duration, statusBarTransitionDelay)); @@ -232,18 +187,19 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag * Otherwise, we will assume we are using a normal app transition, but it's possible that the * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView. */ - private TaskView findTaskViewToLaunch(Launcher launcher, View v) { + private TaskView findTaskViewToLaunch( + BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { if (v instanceof TaskView) { return (TaskView) v; } - if (!launcher.isInState(LauncherState.OVERVIEW)) { - return null; - } + RecentsView recentsView = activity.getOverviewPanel(); + + // It's possible that the launched view can still be resolved to a visible task view, check + // the task id of the opening task and see if we can find a match. if (v.getTag() instanceof ItemInfo) { ItemInfo itemInfo = (ItemInfo) v.getTag(); ComponentName componentName = itemInfo.getTargetComponent(); if (componentName != null) { - RecentsView recentsView = launcher.getOverviewPanel(); for (int i = 0; i < recentsView.getChildCount(); i++) { TaskView taskView = (TaskView) recentsView.getPageAt(i); if (recentsView.isTaskViewVisible(taskView)) { @@ -255,32 +211,10 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag } } } - return null; - } - /** - * Composes the animations for a launch from the recents list if possible. - */ - private LauncherTransitionAnimator composeRecentsLaunchAnimator(View v, - RemoteAnimationTargetCompat[] targets) { - RecentsView recentsView = mLauncher.getOverviewPanel(); - boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING); - MutableBoolean skipLauncherChanges = new MutableBoolean(!launcherClosing); - if (v instanceof TaskView) { - // We already found a task view to launch, so use that for the animation. - TaskView taskView = (TaskView) v; - return new LauncherTransitionAnimator(getRecentsLauncherAnimator(recentsView, taskView), - getRecentsWindowAnimator(taskView, skipLauncherChanges, targets)); - } - - // It's possible that the launched view can still be resolved to a visible task view, check - // the task id of the opening task and see if we can find a match. - - // Ensure recents is actually visible - if (!mLauncher.getStateManager().getState().overviewUi) { + if (targets == null) { return null; } - // Resolve the opening task id int openingTaskId = -1; for (RemoteAnimationTargetCompat target : targets) { @@ -301,19 +235,35 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag if (taskView == null || !recentsView.isTaskViewVisible(taskView)) { return null; } + return taskView; + } + + /** + * Composes the animations for a launch from the recents list if possible. + */ + private Animator[] composeRecentsLaunchAnimator(View v, + RemoteAnimationTargetCompat[] targets) { + // Ensure recents is actually visible + if (!mLauncher.getStateManager().getState().overviewUi) { + return null; + } + + RecentsView recentsView = mLauncher.getOverviewPanel(); + boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING); + boolean skipLauncherChanges = !launcherClosing; + + TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets); + if (taskView == null) { + return null; + } // Found a visible recents task that matches the opening app, lets launch the app from there Animator launcherAnim; - AnimatorListenerAdapter windowAnimEndListener; + final AnimatorListenerAdapter windowAnimEndListener; if (launcherClosing) { launcherAnim = getRecentsLauncherAnimator(recentsView, taskView); - windowAnimEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Make sure recents gets fixed up by resetting task alphas and scales, etc. - mLauncher.getStateManager().reapplyState(); - } - }; + // Make sure recents gets fixed up by resetting task alphas and scales, etc. + windowAnimEndListener = mReapplyStateListener; } else { AnimatorPlaybackController controller = mLauncher.getStateManager() @@ -330,7 +280,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag Animator windowAnim = getRecentsWindowAnimator(taskView, skipLauncherChanges, targets); windowAnim.addListener(windowAnimEndListener); - return new LauncherTransitionAnimator(launcherAnim, windowAnim, skipLauncherChanges); + return new Animator[] {launcherAnim, windowAnim}; } /** @@ -372,26 +322,24 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag } else { // We are launching an adjacent task, so parallax the center and other adjacent task. TaskView centerTask = (TaskView) recentsView.getPageAt(centerTaskIndex); - float translationX = Math.abs(v.getTranslationX()); - ObjectAnimator centerTaskParallaxToRight = + float translationX = mRecentsTransX / 2; + ObjectAnimator centerTaskParallaxOffscreen = LauncherAnimUtils.ofPropertyValuesHolder(centerTask, new PropertyListBuilder() - .scale(v.getScaleX()) .translationX(isRtl ? -translationX : translationX) .build()); - launcherAnimator.play(centerTaskParallaxToRight); + launcherAnimator.play(centerTaskParallaxOffscreen); int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - launchedTaskIndex); if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < recentsView.getPageCount()) { TaskView otherAdjacentTask = (TaskView) recentsView.getPageAt( otherAdjacentTaskIndex); - ObjectAnimator otherAdjacentTaskParallaxToRight = + ObjectAnimator otherAdjacentTaskParallaxOffscreen = LauncherAnimUtils.ofPropertyValuesHolder(otherAdjacentTask, new PropertyListBuilder() - .translationX(otherAdjacentTask.getTranslationX() - + (isRtl ? -translationX : translationX)) + .translationX(isRtl ? -translationX : translationX) .build()); - launcherAnimator.play(otherAdjacentTaskParallaxToRight); + launcherAnimator.play(otherAdjacentTaskParallaxOffscreen); } } @@ -420,7 +368,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag * @return Animator that controls the window of the opening targets for the recents launch * animation. */ - private ValueAnimator getRecentsWindowAnimator(TaskView v, MutableBoolean skipLauncherChanges, + private ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipLauncherChanges, RemoteAnimationTargetCompat[] targets) { Rect taskViewBounds = new Rect(); mDragLayer.getDescendantRectRelativeToSelf(v, taskViewBounds); @@ -456,7 +404,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag final float percent = animation.getAnimatedFraction(); TaskWindowBounds tw = recentsInterpolator.interpolate(percent); - if (!skipLauncherChanges.value) { + if (!skipLauncherChanges) { v.setScaleX(tw.taskScale); v.setScaleY(tw.taskScale); v.setTranslationX(tw.taskX); @@ -471,9 +419,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag crop.set(tw.winCrop); // Fade in the app window. - float alphaDelay = 0; float alphaDuration = 75; - float alpha = getValue(0f, 1f, alphaDelay, alphaDuration, + float alpha = getValue(0f, 1f, 0, alphaDuration, appAnimator.getDuration() * percent, Interpolators.LINEAR); TransactionCompat t = new TransactionCompat(); @@ -487,7 +434,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag t.setMatrix(target.leash, matrix); t.setWindowCrop(target.leash, crop); - if (!skipLauncherChanges.value) { + if (!skipLauncherChanges) { t.deferTransactionUntil(target.leash, surface, frameNumber); } } @@ -504,15 +451,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag return appAnimator; } - /** - * Composes the animations for a launch from an app icon. - */ - private LauncherTransitionAnimator composeAppLaunchAnimator(View v, - RemoteAnimationTargetCompat[] targets) { - return new LauncherTransitionAnimator(getLauncherAnimators(v, targets), - getWindowAnimators(v, targets)); - } - /** * @return Animators that control the movements of the Launcher and icon of the opening target. */ @@ -543,7 +481,9 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) { // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView. - View appsView = mLauncher.getAppsView(); + final View appsView = mLauncher.getAppsView(); + final float startAlpha = appsView.getAlpha(); + final float startY = appsView.getTranslationY(); appsView.setAlpha(alphas[0]); appsView.setTranslationY(trans[0]); @@ -556,6 +496,14 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag launcherAnimator.play(alpha); launcherAnimator.play(transY); + + launcherAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + appsView.setAlpha(startAlpha); + appsView.setTranslationY(startY); + } + }); } else { mDragLayer.setAlpha(alphas[0]); mDragLayer.setTranslationY(trans[0]); @@ -570,6 +518,13 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag launcherAnimator.play(dragLayerAlpha); launcherAnimator.play(dragLayerTransY); + launcherAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDragLayer.setAlpha(1); + mDragLayer.setTranslationY(0); + } + }); } return launcherAnimator; } @@ -588,22 +543,19 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag // Position the floating view exactly on top of the original Rect rect = new Rect(); - final boolean isDeepShortcutTextView = v instanceof DeepShortcutTextView - && v.getParent() != null && v.getParent() instanceof DeepShortcutView; - if (isDeepShortcutTextView) { - // Deep shortcut views have their icon drawn in a sibling view. + final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView; + if (fromDeepShortcutView) { + // Deep shortcut views have their icon drawn in a separate view. DeepShortcutView view = (DeepShortcutView) v.getParent(); mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect); } else { mDragLayer.getDescendantRectRelativeToSelf(v, rect); } - final int viewLocationStart = mIsRtl - ? mDeviceProfile.widthPx - rect.right - : rect.left; - final int viewLocationTop = rect.top; + int viewLocationLeft = rect.left; + int viewLocationTop = rect.top; float startScale = 1f; - if (isBubbleTextView && !isDeepShortcutTextView) { + if (isBubbleTextView && !fromDeepShortcutView) { BubbleTextView btv = (BubbleTextView) v; btv.getIconBounds(rect); Drawable dr = btv.getIcon(); @@ -613,12 +565,24 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag } else { rect.set(0, 0, rect.width(), rect.height()); } + viewLocationLeft += rect.left; + viewLocationTop += rect.top; + int viewLocationStart = mIsRtl + ? mDeviceProfile.widthPx - rect.right + : viewLocationLeft; LayoutParams lp = new LayoutParams(rect.width(), rect.height()); lp.ignoreInsets = true; - lp.setMarginStart(viewLocationStart + rect.left); - lp.topMargin = viewLocationTop + rect.top; + lp.setMarginStart(viewLocationStart); + lp.topMargin = viewLocationTop; mFloatingView.setLayoutParams(lp); + // Set the properties here already to make sure they'are available when running the first + // animation frame. + mFloatingView.setLeft(viewLocationLeft); + mFloatingView.setTop(viewLocationTop); + mFloatingView.setRight(viewLocationLeft + rect.width()); + mFloatingView.setBottom(viewLocationTop + rect.height()); + // Swap the two views in place. ((ViewGroup) mDragLayer.getParent()).addView(mFloatingView); v.setVisibility(View.INVISIBLE); @@ -663,6 +627,14 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag alpha.setInterpolator(Interpolators.LINEAR); appIconAnimatorSet.play(alpha); + appIconAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Reset launcher to normal state + v.setVisibility(View.VISIBLE); + ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView); + } + }); return appIconAnimatorSet; } @@ -671,10 +643,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag */ private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) { Rect bounds = new Rect(); - boolean isDeepShortcutTextView = v instanceof DeepShortcutTextView - && v.getParent() != null && v.getParent() instanceof DeepShortcutView; - if (isDeepShortcutTextView) { - // Deep shortcut views have their icon drawn in a sibling view. + if (v.getParent() instanceof DeepShortcutView) { + // Deep shortcut views have their icon drawn in a separate view. DeepShortcutView view = (DeepShortcutView) v.getParent(); mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds); } else if (v instanceof BubbleTextView) { @@ -728,9 +698,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag matrix.postTranslate(transX0, transY0); // Fade in the app window. - float alphaDelay = 0; float alphaDuration = 60; - float alpha = getValue(0f, 1f, alphaDelay, alphaDuration, + float alpha = getValue(0f, 1f, 0, alphaDuration, appAnimator.getDuration() * percent, Interpolators.LINEAR); // Animate the window crop so that it starts off as a square, and then reveals @@ -775,6 +744,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag try { RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat(); definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN, + WindowManagerWrapper.ACTIVITY_TYPE_STANDARD, new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(), CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */)); @@ -802,40 +772,25 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag * ie. pressing home, swiping up from nav bar. */ private RemoteAnimationRunnerCompat getWallpaperOpenRunner() { - return new LauncherAnimationRunner(mLauncher) { + return new LauncherAnimationRunner(mHandler) { @Override - public void onAnimationStart(RemoteAnimationTargetCompat[] targets, - Runnable finishedCallback) { - Handler handler = mLauncher.getWindow().getDecorView().getHandler(); - postAtFrontOfQueueAsynchronously(handler, () -> { - if ((Utilities.getPrefs(mLauncher) - .getBoolean("pref_use_screenshot_for_swipe_up", false) - && mLauncher.getStateManager().getState().overviewUi) - || !launcherIsATargetWithMode(targets, MODE_OPENING)) { - // We use a separate transition for Overview mode. And we can skip the - // animation in cases where Launcher is not in the set of opening targets. - // This can happen when Launcher is already visible. ie. Closing a dialog. - setCurrentAnimator(null); - finishedCallback.run(); - return; - } + public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) { + if (mLauncher.getStateManager().getState().overviewUi) { + // We use a separate transition for Overview mode. + return null; + } - LauncherTransitionAnimator animator = new LauncherTransitionAnimator( - getLauncherResumeAnimation(), getClosingWindowAnimators(targets)); - setCurrentAnimator(animator); - mAnimator = animator.getAnimatorSet(); - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finishedCallback.run(); - } - }); - mAnimator.start(); + AnimatorSet anim = new AnimatorSet(); + anim.play(getClosingWindowAnimators(targetCompats)); - // Because t=0 has the app icon in its original spot, we can skip the - // first frame and have the same movement one frame earlier. - mAnimator.setCurrentPlayTime(REFRESH_RATE_MS); - }); + if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)) { + AnimatorSet contentAnimation = getLauncherResumeAnimation(); + anim.play(contentAnimation); + + // Only register the content animation for cancellation when state changes + mLauncher.getStateManager().setCurrentAnimation(contentAnimation); + } + return anim; } }; } @@ -906,12 +861,17 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag return contentAnimator; } else { AnimatorSet workspaceAnimator = new AnimatorSet(); + mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY); - mLauncher.getWorkspace().setAlpha(0f); workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.TRANSLATION_Y, mWorkspaceTransY, 0)); - workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.ALPHA, - 0, 1f)); + + View currentPage = ((CellLayout) mLauncher.getWorkspace() + .getChildAt(mLauncher.getWorkspace().getCurrentPage())) + .getShortcutsAndWidgets(); + currentPage.setAlpha(0f); + workspaceAnimator.play(ObjectAnimator.ofFloat(currentPage, View.ALPHA, 0, 1f)); + workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY); workspaceAnimator.setDuration(333); workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); @@ -939,6 +899,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag AnimatorSet resumeLauncherAnimation = new AnimatorSet(); resumeLauncherAnimation.play(workspaceAnimator); resumeLauncherAnimation.playSequentially(allAppsSlideIn, allAppsOvershoot); + + resumeLauncherAnimation.addListener(mReapplyStateListener); return resumeLauncherAnimation; } } diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java new file mode 100644 index 0000000000..0d1038a2d9 --- /dev/null +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3; + +import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; + +import android.annotation.TargetApi; +import android.os.Build; + +import com.android.launcher3.states.InternalStateHandler; +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; + +import java.util.function.BiPredicate; + +@TargetApi(Build.VERSION_CODES.P) +public class LauncherInitListener extends InternalStateHandler implements ActivityInitListener { + + private final BiPredicate mOnInitListener; + + public LauncherInitListener(BiPredicate onInitListener) { + mOnInitListener = onInitListener; + } + + @Override + protected boolean init(Launcher launcher, boolean alreadyOnHome) { + // For the duration of the gesture, lock the screen orientation to ensure that we do not + // rotate mid-quickscrub + launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK); + return mOnInitListener.test(launcher, alreadyOnHome); + } + + @Override + public void register() { + initWhenReady(); + } + + @Override + public void unregister() { + clearReference(); + } +} diff --git a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java b/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java deleted file mode 100644 index ab9234b516..0000000000 --- a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -import android.animation.Animator; -import android.animation.AnimatorSet; - -/** - * Creates an AnimatorSet consisting on one Animator for Launcher transition, and one Animator for - * the Window transitions. - * - * Allows for ending the Launcher animator without ending the Window animator. - */ -public class LauncherTransitionAnimator { - - private final MutableBoolean mLauncherAnimCancelState; - - private AnimatorSet mAnimatorSet; - private Animator mLauncherAnimator; - private Animator mWindowAnimator; - - LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator) { - this(launcherAnimator, windowAnimator, new MutableBoolean(false)); - } - - - LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator, - MutableBoolean launcherAnimCancelState) { - mLauncherAnimCancelState = launcherAnimCancelState; - if (launcherAnimator != null) { - mLauncherAnimator = launcherAnimator; - } - mWindowAnimator = windowAnimator; - - mAnimatorSet = new AnimatorSet(); - if (launcherAnimator != null) { - mAnimatorSet.play(launcherAnimator); - } - mAnimatorSet.play(windowAnimator); - } - - public AnimatorSet getAnimatorSet() { - return mAnimatorSet; - } - - public void cancel() { - mAnimatorSet.cancel(); - mLauncherAnimCancelState.value = true; - } - - public boolean isRunning() { - return mAnimatorSet.isRunning(); - } - - public void finishLauncherAnimation() { - if (mLauncherAnimator != null) { - mLauncherAnimCancelState.value = true; - mLauncherAnimator.end(); - } - } -} diff --git a/quickstep/src/com/android/launcher3/MutableBoolean.java b/quickstep/src/com/android/launcher3/MutableBoolean.java deleted file mode 100644 index 75382171af..0000000000 --- a/quickstep/src/com/android/launcher3/MutableBoolean.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -public class MutableBoolean { - public boolean value; - - public MutableBoolean(boolean value) { - this.value = value; - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java index 31261d9853..2626e7cccc 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java @@ -24,7 +24,6 @@ import android.view.View; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -33,8 +32,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; */ public class AllAppsState extends LauncherState { - private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY - | FLAG_SHOW_SCRIM | FLAG_ALL_APPS_SCRIM; + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_ALL_APPS_SCRIM; private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) { @Override @@ -85,8 +83,15 @@ public class AllAppsState extends LauncherState { } @Override - public float getHoseatAlpha(Launcher launcher) { - return 0; + public int getVisibleElements(Launcher launcher) { + return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT; + } + + @Override + public float[] getOverviewTranslationFactor(Launcher launcher) { + // Keep the same translation as in overview, so that we don't slide around when + // transitioning to All Apps. + return LauncherState.OVERVIEW.getOverviewTranslationFactor(launcher); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java b/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java deleted file mode 100644 index 6df1aba01d..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides; - -import com.android.launcher3.Alarm; -import com.android.launcher3.OnAlarmListener; - -/** - * Utility class to detect a pause during a drag. - */ -public class DragPauseDetector implements OnAlarmListener { - - private static final float MAX_VELOCITY_TO_PAUSE = 0.2f; - private static final long PAUSE_DURATION = 100; - - private final Alarm mAlarm; - private final Runnable mOnPauseCallback; - - private boolean mTriggered = false; - private int mDisabledFlags = 0; - - public DragPauseDetector(Runnable onPauseCallback) { - mOnPauseCallback = onPauseCallback; - - mAlarm = new Alarm(); - mAlarm.setOnAlarmListener(this); - mAlarm.setAlarm(PAUSE_DURATION); - } - - public void onDrag(float velocity) { - if (mTriggered || !isEnabled()) { - return; - } - - if (Math.abs(velocity) > MAX_VELOCITY_TO_PAUSE) { - // Cancel any previous alarm and set a new alarm - mAlarm.setAlarm(PAUSE_DURATION); - } - } - - @Override - public void onAlarm(Alarm alarm) { - if (!mTriggered && isEnabled()) { - mTriggered = true; - mOnPauseCallback.run(); - } - } - - public boolean isTriggered () { - return mTriggered; - } - - public boolean isEnabled() { - return mDisabledFlags == 0; - } - - public void addDisabledFlags(int flags) { - boolean wasEnabled = isEnabled(); - mDisabledFlags |= flags; - resetAlarm(wasEnabled); - } - - public void clearDisabledFlags(int flags) { - boolean wasEnabled = isEnabled(); - mDisabledFlags &= ~flags; - resetAlarm(wasEnabled); - } - - private void resetAlarm(boolean wasEnabled) { - boolean isEnabled = isEnabled(); - if (wasEnabled == isEnabled) { - // Nothing has changed - } if (isEnabled && !mTriggered) { - mAlarm.setAlarm(PAUSE_DURATION); - } else if (!isEnabled) { - mAlarm.cancelAlarm(); - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java deleted file mode 100644 index 97ac3e6069..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides; - -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE; -import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE; -import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL; -import static com.android.launcher3.touch.SwipeDetector.VERTICAL; -import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; - -import android.graphics.Rect; -import android.metrics.LogMaker; -import android.view.MotionEvent; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; -import com.android.quickstep.views.RecentsView; - -class EventLogTags { - private EventLogTags() { - } // don't instantiate - - /** 524292 sysui_multi_action (content|4) */ - public static final int SYSUI_MULTI_ACTION = 524292; - - public static void writeSysuiMultiAction(Object[] content) { - android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content); - } -} - -class MetricsLogger { - private static MetricsLogger sMetricsLogger; - - private static MetricsLogger getLogger() { - if (sMetricsLogger == null) { - sMetricsLogger = new MetricsLogger(); - } - return sMetricsLogger; - } - - protected void saveLog(Object[] rep) { - EventLogTags.writeSysuiMultiAction(rep); - } - - public void write(LogMaker content) { - if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { - content.setType(4/*MetricsEvent.TYPE_ACTION*/); - } - saveLog(content.serialize()); - } -} - -/** - * Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW. - */ -public class EdgeSwipeController extends VerticalSwipeController implements - OnDeviceProfileChangeListener { - - private static final Rect sTempRect = new Rect(); - - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - - public EdgeSwipeController(Launcher l) { - super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout() - ? HORIZONTAL : VERTICAL); - l.addOnDeviceProfileChangeListener(this); - } - - @Override - public void onDeviceProfileChanged(DeviceProfile dp) { - mDetector.updateDirection(dp.isVerticalBarLayout() ? HORIZONTAL : VERTICAL); - } - - @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { - return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; - } - - @Override - protected int getSwipeDirection(MotionEvent ev) { - return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE; - } - - public EdgeSwipeController(Launcher l, LauncherState baseState) { - super(l, baseState); - } - - @Override - protected boolean isTransitionFlipped() { - return mLauncher.getDeviceProfile().isSeascape(); - } - - @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged && mToState instanceof OverviewState) { - // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for - // "Recents" activity for app transition tests. - final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); - builder.setPackageName("com.android.systemui"); - builder.addTaggedData(871/*FIELD_CLASS_NAME*/, - "com.android.systemui.recents.RecentsActivity"); - builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, - 0/* zero time */); - mMetricsLogger.write(builder); - - // Add user event logging for launcher pipeline - int direction = Direction.UP; - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - direction = Direction.LEFT; - if (mLauncher.getDeviceProfile().isSeascape()) { - direction = Direction.RIGHT; - } - } - mLauncher.getUserEventDispatcher().logStateChangeAction( - wasFling ? Touch.FLING : Touch.SWIPE, direction, - ContainerType.NAVBAR, ContainerType.WORKSPACE, // src target - ContainerType.TASKSWITCHER, // dst target - mLauncher.getWorkspace().getCurrentPage()); - } - } - - @Override - protected float getShiftRange() { - return getShiftRange(mLauncher); - } - - public static float getShiftRange(Launcher launcher) { - RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, sTempRect); - DragLayer dl = launcher.getDragLayer(); - Rect insets = dl.getInsets(); - DeviceProfile dp = launcher.getDeviceProfile(); - - if (dp.isVerticalBarLayout()) { - if (dp.isSeascape()) { - return insets.left + sTempRect.left; - } else { - return dl.getWidth() - sTempRect.right + insets.right; - } - } else { - return dl.getHeight() - sTempRect.bottom + insets.bottom; - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java index 9541d0d052..99bf2649b9 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.quickstep.QuickScrubController; import com.android.quickstep.views.RecentsView; @@ -25,12 +26,10 @@ import com.android.quickstep.views.RecentsView; public class FastOverviewState extends OverviewState { private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_DISABLE_RESTORE - | FLAG_DISABLE_INTERACTION | FLAG_OVERVIEW_UI; - - private static final boolean DEBUG_DIFFERENT_UI = false; + | FLAG_DISABLE_INTERACTION | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON; public FastOverviewState(int id) { - super(id, QuickScrubController.QUICK_SWITCH_START_DURATION, STATE_FLAGS); + super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS); } @Override @@ -40,11 +39,18 @@ public class FastOverviewState extends OverviewState { recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub(); } + public void onStateEnabled(Launcher launcher) { + super.onStateEnabled(launcher); + AbstractFloatingView.closeAllOpenViews(launcher); + } + @Override - public float getHoseatAlpha(Launcher launcher) { - if (DEBUG_DIFFERENT_UI) { - return 0; - } - return super.getHoseatAlpha(launcher); + public int getVisibleElements(Launcher launcher) { + return NONE; + } + + @Override + public float[] getOverviewTranslationFactor(Launcher launcher) { + return new float[] {0f, 0.5f}; } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java new file mode 100644 index 0000000000..23add9595c --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java @@ -0,0 +1,71 @@ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; + +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.quickstep.util.SysuiEventLogger; + +/** + * Touch controller for handling edge swipes in landscape/seascape UI + */ +public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController { + + public LandscapeEdgeSwipeController(Launcher l) { + super(l, SwipeDetector.HORIZONTAL); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; + } + + @Override + protected int getSwipeDirection(MotionEvent ev) { + mFromState = NORMAL; + mToState = OVERVIEW; + return SwipeDetector.DIRECTION_BOTH; + } + + @Override + protected float getShiftRange() { + return mLauncher.getDragLayer().getWidth(); + } + + @Override + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range; + } + + @Override + protected int getDirectionForLog() { + return mLauncher.getDeviceProfile().isSeascape() ? Direction.RIGHT : Direction.LEFT; + } + + @Override + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + super.onSwipeInteractionCompleted(targetState, logAction); + if (mFromState == NORMAL && targetState == OVERVIEW) { + SysuiEventLogger.writeDummyRecentsTransition(0); + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java new file mode 100644 index 0000000000..720b20ac1b --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.views.RecentsView; + +/** + * Touch controller from going from OVERVIEW to ALL_APPS + */ +public class LandscapeStatesTouchController extends PortraitStatesTouchController { + + public LandscapeStatesTouchController(Launcher l) { + super(l); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + if (mLauncher.isInState(ALL_APPS)) { + // In all-apps only listen if the container cannot scroll itself + return mLauncher.getAppsView().shouldContainerScroll(ev); + } else if (mLauncher.isInState(NORMAL)) { + return true; + } else if (mLauncher.isInState(OVERVIEW)) { + RecentsView rv = mLauncher.getOverviewPanel(); + return ev.getY() > (rv.getBottom() - rv.getPaddingBottom()); + } else { + return false; + } + } + + protected LauncherState getTargetState() { + if (mLauncher.isInState(ALL_APPS)) { + // Should swipe down go to OVERVIEW instead? + return TouchInteractionService.isConnected() ? + mLauncher.getStateManager().getLastState() : NORMAL; + } else { + return ALL_APPS; + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index abb4ecf4c9..a72b1b15bf 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -22,6 +22,7 @@ import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; import android.graphics.Rect; import android.view.View; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; @@ -56,6 +57,11 @@ public class OverviewState extends LauncherState { return getScaleAndTranslationForPageRect(launcher, pageRect); } + @Override + public float[] getOverviewTranslationFactor(Launcher launcher) { + return new float[] {0f, 0f}; + } + @Override public void onStateEnabled(Launcher launcher) { RecentsView rv = launcher.getOverviewPanel(); @@ -100,4 +106,29 @@ public class OverviewState extends LauncherState { return new float[] {scale, 0, translationY}; } + + @Override + public int getVisibleElements(Launcher launcher) { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + return NONE; + } else { + return launcher.getAppsView().getFloatingHeaderView().hasVisibleContent() + ? HOTSEAT_EXTRA | ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS | HOTSEAT_EXTRA; + } + } + + @Override + public float getVerticalProgress(Launcher launcher) { + if ((getVisibleElements(launcher) & ALL_APPS_HEADER_EXTRA) == 0) { + // We have no all apps content, so we're still at the fully down progress. + return super.getVerticalProgress(launcher); + } + return 1 - (getDefaultSwipeHeight(launcher) + / launcher.getAllAppsController().getShiftRange()); + } + + public static float getDefaultSwipeHeight(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java deleted file mode 100644 index 4fb388613e..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides; - -import static com.android.launcher3.LauncherState.OVERVIEW; - -import android.view.MotionEvent; - -import com.android.launcher3.Launcher; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; - -/** - * Extension of {@link VerticalSwipeController} which allows swipe up from OVERVIEW to ALL_APPS - * Note that the swipe down is handled by {@link TwoStepSwipeController}. - */ -public class OverviewSwipeUpController extends VerticalSwipeController { - - public OverviewSwipeUpController(Launcher l) { - super(l, OVERVIEW); - } - - @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { - if (!mLauncher.isInState(OVERVIEW)) { - return false; - } - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return ev.getY() > - mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher); - } else { - return mLauncher.getDragLayer().isEventOverHotseat(ev); - } - } - - @Override - protected int getSwipeDirection(MotionEvent ev) { - return SwipeDetector.DIRECTION_POSITIVE; - } - - @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logStateChangeAction( - wasFling ? Touch.FLING : Touch.SWIPE, - Direction.UP, - ContainerType.HOTSEAT, - ContainerType.TASKSWITCHER, - ContainerType.ALLAPPS, - mLauncher.getWorkspace().getCurrentPage()); - } - - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java new file mode 100644 index 0000000000..1e006e5752 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.view.MotionEvent; +import android.view.animation.Interpolator; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.SysuiEventLogger; + +/** + * Touch controller for handling various state transitions in portrait UI. + */ +public class PortraitStatesTouchController extends AbstractStateChangeTouchController { + + private static final float TOTAL_DISTANCE_MULTIPLIER = 2f; + private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER; + + // Much be greater than LINEAR_SCALE_LIMIT; + private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f; + + // Maximum amount to overshoot. + private static final float MAX_OVERSHOOT = 0.3f; + + private static final double PI_BY_2 = Math.PI / 2; + + private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper(); + + // If > 0, the animation progress is clamped at that value as long as user is dragging. + private float mClampProgressUpdate = -1; + + // If true, we will finish the current animation instantly on second touch. + private boolean mFinishFastOnSecondTouch; + + private final Interpolator mAllAppsDampedInterpolator = new Interpolator() { + + private final double mAngleMultiplier = Math.PI / + (2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT)); + + @Override + public float getInterpolation(float v) { + if (v <= LINEAR_SCALE_LIMIT) { + return v * TOTAL_DISTANCE_MULTIPLIER; + } + float overshoot = (v - LINEAR_SCALE_LIMIT); + return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier)); + } + }; + + private final Interpolator mOverviewBoundInterpolator = (v) -> { + if (v >= MAXIMUM_DISTANCE_FACTOR) { + return 1; + } + return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR); + }; + + public PortraitStatesTouchController(Launcher l) { + super(l, SwipeDetector.VERTICAL); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + if (mCurrentAnimation != null) { + if (mFinishFastOnSecondTouch) { + // TODO: Animate to finish instead. + mCurrentAnimation.getAnimationPlayer().end(); + } + + // If we are already animating from a previous state, we can intercept. + return true; + } + if (mLauncher.isInState(ALL_APPS)) { + // In all-apps only listen if the container cannot scroll itself + if (!mLauncher.getAppsView().shouldContainerScroll(ev)) { + return false; + } + } else { + // For all other states, only listen if the event originated below the hotseat height + DeviceProfile dp = mLauncher.getDeviceProfile(); + int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; + if (ev.getY() < (mLauncher.getDragLayer().getHeight() - hotseatHeight)) { + return false; + } + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + return true; + } + + @Override + protected int getSwipeDirection(MotionEvent ev) { + final int directionsToDetectScroll; + if (mLauncher.isInState(ALL_APPS)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + mStartContainerType = ContainerType.ALLAPPS; + } else if (mLauncher.isInState(NORMAL)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = ContainerType.HOTSEAT; + } else if (mLauncher.isInState(OVERVIEW)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = ContainerType.TASKSWITCHER; + } else { + return 0; + } + mFromState = mLauncher.getStateManager().getState(); + mToState = getTargetState(); + if (mFromState == mToState) { + return 0; + } + return directionsToDetectScroll; + } + + protected LauncherState getTargetState() { + if (mLauncher.isInState(ALL_APPS)) { + // Should swipe down go to OVERVIEW instead? + return TouchInteractionService.isConnected() ? + mLauncher.getStateManager().getLastState() : NORMAL; + } else if (mLauncher.isInState(OVERVIEW)) { + return ALL_APPS; + } else { + return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS; + } + } + + private AnimatorSetBuilder getNormalToOverviewAnimation() { + mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator; + + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper); + + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator); + return builder; + } + + @Override + protected void updateProgress(float fraction) { + if (mClampProgressUpdate > 0) { + mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate)); + } else { + super.updateProgress(fraction); + } + } + + @Override + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + + float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; + float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; + + float totalShift = endVerticalShift - startVerticalShift; + + final AnimatorSetBuilder builder; + + if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) { + builder = getNormalToOverviewAnimation(); + totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER; + mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR; + } else { + builder = new AnimatorSetBuilder(); + mClampProgressUpdate = -1; + } + + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, builder, maxAccuracy); + + if (totalShift == 0) { + totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) + * OverviewState.getDefaultSwipeHeight(mLauncher); + } + return 1 / totalShift; + } + + @Override + protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, + LauncherState targetState, float velocity, boolean isFling) { + if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) { + mFinishFastOnSecondTouch = true; + + // Update all apps interpolator + float currentFraction = mCurrentAnimation.getProgressFraction(); + float absVelocity = Math.abs(velocity); + float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction); + + if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) { + + // TODO: Clean up these magic calculations + // Linearly interpolate the max value based on the velocity. + float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT : + 1 + (absVelocity - 1) * MAX_OVERSHOOT / 3, + currentValue); + double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue); + + if (expectedDuration != 0 && angleToPeak != 0) { + + float distanceLeft = 1 - currentFraction; + mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { + float scaledF = (f - currentFraction) / distanceLeft; + + if (scaledF < 0.5f) { + double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f; + return (float) (maxValue * Math.sin(angle)); + } + + scaledF = ((scaledF - .5f) / .5f); + double angle = PI_BY_2 + 3 * scaledF * PI_BY_2; + float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1); + return 1 + (float) (amplitude * Math.sin(angle)); + }; + + animator.setDuration(expectedDuration).setInterpolator(LINEAR); + return; + } + } + + if (currentFraction < LINEAR_SCALE_LIMIT) { + mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR; + super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, + velocity, isFling); + return; + } + float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1; + float distanceLeft = 1 - currentFraction; + + animator.setFloatValues(currentFraction, 1); + mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { + float scaledF = (f - currentFraction) / distanceLeft; + + double angle = scaledF * 1.5 * Math.PI; + float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue; + return 1 + (float) (amplitude * Math.sin(angle)); + }; + animator.setDuration(200).setInterpolator(LINEAR); + return; + } + mFinishFastOnSecondTouch = false; + super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, + velocity, isFling); + } + + @Override + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + super.onSwipeInteractionCompleted(targetState, logAction); + if (mFromState == NORMAL && targetState == OVERVIEW) { + SysuiEventLogger.writeDummyRecentsTransition(0); + } + } + + private static class InterpolatorWrapper implements Interpolator { + + public TimeInterpolator baseInterpolator = LINEAR; + + @Override + public float getInterpolation(float v) { + return baseInterpolator.getInterpolation(v); + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index b7f79b321c..d8f206c5ed 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -16,36 +16,32 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; +import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_X_FACTOR; +import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR; +import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.view.View; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.os.Build; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.PagedView; -import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.anim.Interpolators; -import com.android.quickstep.AnimatedFloat; -import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; +import com.android.launcher3.anim.PropertySetter; +import com.android.quickstep.views.LauncherRecentsView; +@TargetApi(Build.VERSION_CODES.O) public class RecentsViewStateController implements StateHandler { private final Launcher mLauncher; - private final RecentsView mRecentsView; - - private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress); - // The fraction representing the visibility of the RecentsView. This allows delaying the - // overall transition while the RecentsView is being shown or hidden. - private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress); - - private boolean mIsRecentsSlidingInOrOut; + private final LauncherRecentsView mRecentsView; public RecentsViewStateController(Launcher launcher) { mLauncher = launcher; @@ -54,8 +50,11 @@ public class RecentsViewStateController implements StateHandler { @Override public void setState(LauncherState state) { - setVisibility(state.overviewUi); - setTransitionProgress(state.overviewUi ? 1 : 0); + mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0); + updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher)); + float[] translationFactor = state.getOverviewTranslationFactor(mLauncher); + mRecentsView.setTranslationXFactor(translationFactor[0]); + mRecentsView.setTranslationYFactor(translationFactor[1]); if (state.overviewUi) { mRecentsView.resetTaskVisuals(); } @@ -64,11 +63,9 @@ public class RecentsViewStateController implements StateHandler { @Override public void setStateWithAnimation(final LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { - LauncherState fromState = mLauncher.getStateManager().getState(); - mIsRecentsSlidingInOrOut = fromState == NORMAL && toState.overviewUi - || fromState.overviewUi && toState == NORMAL; // Scroll to the workspace card before changing to the NORMAL state. + LauncherState fromState = mLauncher.getStateManager().getState(); int currPage = mRecentsView.getCurrentPage(); if (fromState.overviewUi && toState == NORMAL && currPage != 0 && !config.userControlled) { int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION; @@ -79,78 +76,24 @@ public class RecentsViewStateController implements StateHandler { builder.setStartDelay(snapDuration / 4); } - ObjectAnimator progressAnim = - mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0); - progressAnim.setDuration(config.duration); - progressAnim.setInterpolator(Interpolators.LINEAR); - progressAnim.addListener(new AnimationSuccessListener() { + PropertySetter setter = config.getProperSetter(builder); + float[] translationFactor = toState.getOverviewTranslationFactor(mLauncher); + setter.setFloat(mRecentsView, TRANSLATION_X_FACTOR, + translationFactor[0], + builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); + setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, + translationFactor[1], + builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); + setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0, LINEAR); - @Override - public void onAnimationSuccess(Animator animator) { - mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen()); - } - }); - builder.play(progressAnim); - - ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi); - visibilityAnim.setDuration(config.duration); - visibilityAnim.setInterpolator(Interpolators.LINEAR); - builder.play(visibilityAnim); - } - - public void setVisibility(boolean isVisible) { - mVisibilityMultiplier.cancelAnimation(); - mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE); - mVisibilityMultiplier.updateValue(isVisible ? 1 : 0); - } - - public ObjectAnimator animateVisibility(boolean isVisible) { - ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0); - if (isVisible) { - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mRecentsView.setVisibility(View.VISIBLE); - } + if (toState.overviewUi) { + ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1); + updateAnim.addUpdateListener(valueAnimator -> { + // While animating into recents, update the visible task data as needed + mRecentsView.loadVisibleTaskData(); }); - } else { - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mRecentsView.setVisibility(View.GONE); - } - }); - } - return anim; - } - - public void setTransitionProgress(float progress) { - mTransitionProgress.cancelAnimation(); - mTransitionProgress.updateValue(progress); - } - - private void onTransitionProgress() { - applyProgress(); - if (mIsRecentsSlidingInOrOut) { - float interpolatedProgress = ACCEL.getInterpolation(mTransitionProgress.value); - // Slide in from the side as we swipe. - int translation = mRecentsView.getWidth(); - if (mRecentsView.isRtl()) { - translation = -translation; - } - mRecentsView.setTranslationX(translation * (1 - interpolatedProgress)); - } - } - - private void onVisibilityProgress() { - applyProgress(); - } - - private void applyProgress() { - mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value); - if (mIsRecentsSlidingInOrOut) { - // While animating into recents, update the visible task data as needed - mRecentsView.loadVisibleTaskData(); + updateAnim.setDuration(config.duration); + builder.play(updateAnim); } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java b/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java deleted file mode 100644 index 651a753544..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides; - -import android.animation.Animator; -import android.util.SparseArray; - -import com.android.launcher3.anim.AnimatorSetBuilder; - -import java.util.Collections; -import java.util.List; - -public class TaggedAnimatorSetBuilder extends AnimatorSetBuilder { - - /** - * Map of the index in {@link #mAnims} to tag. All the animations in {@link #mAnims} starting - * from this index correspond to the tag (until a new tag is specified for an index) - */ - private final SparseArray mTags = new SparseArray<>(); - - @Override - public void startTag(Object obj) { - mTags.put(mAnims.size(), obj); - } - - public List getAnimationsForTag(Object tag) { - int startIndex = mTags.indexOfValue(tag); - if (startIndex < 0) { - return Collections.emptyList(); - } - int startPos = mTags.keyAt(startIndex); - - int endIndex = startIndex + 1; - int endPos = endIndex >= mTags.size() ? mAnims.size() : mTags.keyAt(endIndex); - - return mAnims.subList(startPos, endPos); - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java similarity index 72% rename from quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java rename to quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index c8b54adaf0..d11547de84 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; @@ -30,24 +28,21 @@ import android.view.View; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.touch.SwipeDetector; -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.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TouchController; import com.android.quickstep.PendingAnimation; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; /** - * Touch controller for swipe interaction in Overview state + * Touch controller for handling task view card swipes */ -public class OverviewSwipeController extends AnimatorListenerAdapter +public class TaskViewTouchController extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener { private static final String TAG = "OverviewSwipeController"; @@ -68,16 +63,14 @@ public class OverviewSwipeController extends AnimatorListenerAdapter private boolean mCurrentAnimationIsGoingUp; private boolean mNoIntercept; - private boolean mSwipeDownEnabled; private float mDisplacementShift; private float mProgressMultiplier; private float mEndDisplacement; - private int mStartingTarget; private TaskView mTaskBeingDragged; - public OverviewSwipeController(Launcher launcher) { + public TaskViewTouchController(Launcher launcher) { mLauncher = launcher; mRecentsView = launcher.getOverviewPanel(); mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL); @@ -94,15 +87,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter return mLauncher.isInState(OVERVIEW); } - private boolean isEventOverHotseat(MotionEvent ev) { - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return ev.getY() > - mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher); - } else { - return mLauncher.getDragLayer().isEventOverHotseat(ev); - } - } - @Override public void onAnimationCancel(Animator animation) { if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { @@ -129,22 +113,14 @@ public class OverviewSwipeController extends AnimatorListenerAdapter ignoreSlopWhenSettling = true; } else { mTaskBeingDragged = null; - mSwipeDownEnabled = true; View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage()); if (view instanceof TaskView && mLauncher.getDragLayer().isEventOverView(view, ev)) { // The tile can be dragged down to open the task. mTaskBeingDragged = (TaskView) view; directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; - mStartingTarget = LauncherLogProto.ItemType.TASK; - } else if (isEventOverHotseat(ev)) { - // The hotseat is being dragged - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - mSwipeDownEnabled = false; - mStartingTarget = ContainerType.HOTSEAT; } else { mNoIntercept = true; - mStartingTarget = ContainerType.WORKSPACE; return false; } } @@ -167,9 +143,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter } private void reInitAnimationController(boolean goingUp) { - if (!goingUp && !mSwipeDownEnabled) { - goingUp = true; - } if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) { // No need to init return; @@ -187,31 +160,20 @@ public class OverviewSwipeController extends AnimatorListenerAdapter long maxDuration = (long) (2 * range); DragLayer dl = mLauncher.getDragLayer(); - if (mTaskBeingDragged == null) { - // User is either going to all apps or home - mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(goingUp ? ALL_APPS : NORMAL, maxDuration); - if (goingUp) { - mEndDisplacement = -range; - } else { - mEndDisplacement = EdgeSwipeController.getShiftRange(mLauncher); - } + if (goingUp) { + mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, + true /* animateTaskView */, true /* removeTask */, maxDuration); + mCurrentAnimation = AnimatorPlaybackController + .wrap(mPendingAnimation.anim, maxDuration); + mEndDisplacement = -mTaskBeingDragged.getHeight(); } else { - if (goingUp) { - mPendingAnimation = mRecentsView - .createTaskDismissAnimation(mTaskBeingDragged, maxDuration); - mCurrentAnimation = AnimatorPlaybackController - .wrap(mPendingAnimation.anim, maxDuration); - mEndDisplacement = -mTaskBeingDragged.getHeight(); - } else { - AnimatorSet anim = new AnimatorSet(); - // TODO: Setup a zoom animation - mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); + AnimatorSet anim = new AnimatorSet(); + // TODO: Setup a zoom animation + mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); - mTempCords[1] = mTaskBeingDragged.getHeight(); - dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); - mEndDisplacement = dl.getHeight() - mTempCords[1]; - } + mTempCords[1] = mTaskBeingDragged.getHeight(); + dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); + mEndDisplacement = dl.getHeight() - mTempCords[1]; } mCurrentAnimation.getTarget().addListener(this); @@ -249,9 +211,7 @@ public class OverviewSwipeController extends AnimatorListenerAdapter if (fling) { logAction = Touch.FLING; boolean goingUp = velocity < 0; - if (!goingUp && !mSwipeDownEnabled) { - goingToEnd = false; - } else if (goingUp != mCurrentAnimationIsGoingUp) { + if (goingUp != mCurrentAnimationIsGoingUp) { // In case the fling is in opposite direction, make sure if is close enough // from the start position if (mCurrentAnimation.getProgressFraction() @@ -277,7 +237,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter float nextFrameProgress = Utilities.boundToRange( progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f); - mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction)); ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); @@ -292,25 +251,13 @@ public class OverviewSwipeController extends AnimatorListenerAdapter mPendingAnimation.finish(wasSuccess); mPendingAnimation = null; } - if (mTaskBeingDragged == null) { - LauncherState state = wasSuccess ? - (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW; - mLauncher.getStateManager().goToState(state, false); - - } else if (wasSuccess) { + if (wasSuccess) { if (!mCurrentAnimationIsGoingUp) { mTaskBeingDragged.launchTask(false); mLauncher.getUserEventDispatcher().logTaskLaunch(logAction, Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent()); } } - if (mTaskBeingDragged == null || (wasSuccess && mCurrentAnimationIsGoingUp)) { - mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, - mCurrentAnimationIsGoingUp ? Direction.UP : Direction.DOWN, - mStartingTarget, ContainerType.TASKSWITCHER, - mLauncher.getStateManager().getState().containerType, - mRecentsView.getCurrentPage()); - } mDetector.finishedScrolling(); mTaskBeingDragged = null; mCurrentAnimation = null; diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java deleted file mode 100644 index c8d75dc6f6..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides; - -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; -import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.util.Log; -import android.view.MotionEvent; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateManager; -import com.android.launcher3.LauncherStateManager.AnimationConfig; -import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.FloatRange; -import com.android.launcher3.util.TouchController; -import com.android.quickstep.TouchInteractionService; - -/** - * Handles vertical touch gesture on the DragLayer - */ -public class TwoStepSwipeController extends AnimatorListenerAdapter - implements TouchController, SwipeDetector.Listener { - - private static final String TAG = "TwoStepSwipeController"; - - private static final float RECATCH_REJECTION_FRACTION = .0875f; - private static final int SINGLE_FRAME_MS = 16; - private static final long QUICK_SNAP_TO_OVERVIEW_DURATION = 250; - - // Progress after which the transition is assumed to be a success in case user does not fling - private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; - - /** - * Index of the vertical swipe handles in {@link LauncherStateManager#getStateHandlers()}. - */ - private static final int SWIPE_HANDLER_INDEX = 0; - - /** - * Index of various UI handlers in {@link LauncherStateManager#getStateHandlers()} not related - * to vertical swipe. - */ - private static final int OTHER_HANDLERS_START_INDEX = SWIPE_HANDLER_INDEX + 1; - - // Swipe progress range (when starting from NORMAL state) where OVERVIEW state is allowed - private static final float MIN_PROGRESS_TO_OVERVIEW = 0.1f; - private static final float MAX_PROGRESS_TO_OVERVIEW = 0.4f; - - private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0; - private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1; - private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2; - private static final int FLAG_OVERVIEW_DISABLED = 1 << 4; - private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5; - private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6; - - private final Launcher mLauncher; - private final SwipeDetector mDetector; - - private boolean mNoIntercept; - private int mStartContainerType; - - private DragPauseDetector mDragPauseDetector; - private FloatRange mOverviewProgressRange; - private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder; - private AnimatorSet mQuickOverviewAnimation; - private boolean mAnimatingToOverview; - private CroppedAnimationController mCroppedAnimationController; - - private AnimatorPlaybackController mCurrentAnimation; - private LauncherState mFromState; - private LauncherState mToState; - - private float mStartProgress; - // Ratio of transition process [0, 1] to drag displacement (px) - private float mProgressMultiplier; - - public TwoStepSwipeController(Launcher l) { - mLauncher = l; - mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); - } - - private boolean canInterceptTouch(MotionEvent ev) { - if (mCurrentAnimation != null) { - // If we are already animating from a previous state, we can intercept. - return true; - } - if (mLauncher.isInState(NORMAL)) { - if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 && - !mLauncher.getDeviceProfile().isVerticalBarLayout()) { - // On normal swipes ignore edge swipes - return false; - } - } else if (mLauncher.isInState(ALL_APPS)) { - if (!mLauncher.getAppsView().shouldContainerScroll(ev)) { - return false; - } - } else { - // Don't listen for the swipe gesture if we are already in some other state. - return false; - } - if (mAnimatingToOverview) { - return false; - } - if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { - return false; - } - - return true; - } - - @Override - public void onAnimationCancel(Animator animation) { - if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) { - Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); - clearState(); - } - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = !canInterceptTouch(ev); - if (mNoIntercept) { - return false; - } - - // Now figure out which direction scroll events the controller will start - // calling the callbacks. - final int directionsToDetectScroll; - boolean ignoreSlopWhenSettling = false; - - if (mCurrentAnimation != null) { - if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) { - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) { - directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; - } else { - directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; - ignoreSlopWhenSettling = true; - } - } else { - if (mLauncher.isInState(ALL_APPS)) { - directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; - mStartContainerType = ContainerType.ALLAPPS; - } else { - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? - ContainerType.HOTSEAT : ContainerType.WORKSPACE; - } - } - - mDetector.setDetectableScrollConditions( - directionsToDetectScroll, ignoreSlopWhenSettling); - } - - if (mNoIntercept) { - return false; - } - - onControllerTouchEvent(ev); - return mDetector.isDraggingOrSettling(); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - return mDetector.onTouchEvent(ev); - } - - @Override - public void onDragStart(boolean start) { - if (mCurrentAnimation == null) { - float range = getShiftRange(); - long maxAccuracy = (long) (2 * range); - - mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected); - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) { - mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS); - } - - mOverviewProgressRange = new FloatRange(); - mOverviewProgressRange.start = mLauncher.isInState(NORMAL) - ? MIN_PROGRESS_TO_OVERVIEW - : 1 - MAX_PROGRESS_TO_OVERVIEW; - mOverviewProgressRange.end = mOverviewProgressRange.start - + MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW; - - // Build current animation - mFromState = mLauncher.getStateManager().getState(); - mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS; - - if (mToState == NORMAL && mLauncher.getStateManager().getLastState() == OVERVIEW) { - mToState = OVERVIEW; - mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_BACK_TARGET); - } - - mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder(); - mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace( - mToState, mTaggedAnimatorSetBuilder, maxAccuracy); - - if (!TouchInteractionService.isConnected()) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED); - } - - mCurrentAnimation.getTarget().addListener(this); - mStartProgress = 0; - mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range; - mCurrentAnimation.dispatchOnStart(); - } else { - mCurrentAnimation.pause(); - mStartProgress = mCurrentAnimation.getProgressFraction(); - - mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING); - updatePauseDetectorRangeFlag(); - } - } - - private float getShiftRange() { - return mLauncher.getAllAppsController().getShiftRange(); - } - - @Override - public boolean onDrag(float displacement, float velocity) { - float deltaProgress = mProgressMultiplier * displacement; - mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress); - - updatePauseDetectorRangeFlag(); - mDragPauseDetector.onDrag(velocity); - - return true; - } - - private void updatePauseDetectorRangeFlag() { - if (mOverviewProgressRange.contains(mCurrentAnimation.getProgressFraction())) { - mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - } else { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - } - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING); - - final int logAction; - LauncherState targetState; - final float progress = mCurrentAnimation.getProgressFraction(); - - if (fling) { - logAction = Touch.FLING; - targetState = velocity < 0 ? ALL_APPS : mLauncher.getStateManager().getLastState(); - // snap to top or bottom using the release velocity - } else { - logAction = Touch.SWIPE; - targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; - } - - float endProgress; - - if (mDragPauseDetector.isTriggered() && targetState == NORMAL) { - targetState = OVERVIEW; - endProgress = OVERVIEW.getVerticalProgress(mLauncher); - if (mFromState == NORMAL) { - endProgress = 1 - endProgress; - } - } else if (targetState == mToState) { - endProgress = 1; - } else { - endProgress = 0; - } - - LauncherState targetStateFinal = targetState; - mCurrentAnimation.setEndAction(() -> - onSwipeInteractionCompleted(targetStateFinal, logAction)); - - float nextFrameProgress = Utilities.boundToRange( - progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); - - ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); - anim.setFloatValues(nextFrameProgress, endProgress); - anim.setDuration( - SwipeDetector.calculateDuration(velocity, Math.abs(endProgress - progress))); - anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); - anim.start(); - } - - private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { - if (targetState != mFromState) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, - mToState == ALL_APPS ? Direction.UP : Direction.DOWN, - mStartContainerType, - mFromState.containerType, - mToState.containerType, - mLauncher.getWorkspace().getCurrentPage()); - } - clearState(); - - // TODO: mQuickOverviewAnimation might still be running in which changing a state instantly - // may cause a jump. Animate the state change with a short duration in this case? - mLauncher.getStateManager().goToState(targetState, false /* animated */); - } - - private void onDragPauseDetected() { - final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1); - twoStepAnimator.setDuration(mCurrentAnimation.getDuration()); - StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers(); - - // Change the current animation to only play the vertical handle - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(mTaggedAnimatorSetBuilder.getAnimationsForTag( - handlers[SWIPE_HANDLER_INDEX])); - anim.play(twoStepAnimator); - mCurrentAnimation = mCurrentAnimation.cloneFor(anim); - - AnimatorSetBuilder builder = new AnimatorSetBuilder(); - AnimationConfig config = new AnimationConfig(); - config.duration = QUICK_SNAP_TO_OVERVIEW_DURATION; - for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) { - handlers[i].setStateWithAnimation(OVERVIEW, builder, config); - } - mQuickOverviewAnimation = builder.build(); - mQuickOverviewAnimation.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - onQuickOverviewAnimationComplete(twoStepAnimator); - } - }); - mQuickOverviewAnimation.start(); - } - - private void onQuickOverviewAnimationComplete(ValueAnimator animator) { - if (mAnimatingToOverview) { - return; - } - - // For the remainder to the interaction, the user can either go to the ALL_APPS state or - // the OVERVIEW state. - // The remaining state handlers are on the OVERVIEW state. Create one animation towards the - // ALL_APPS state and only call it when the user moved above the current range. - AnimationConfig config = new AnimationConfig(); - config.duration = (long) (2 * getShiftRange()); - config.userControlled = true; - - AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder(); - StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers(); - for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) { - handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config); - } - - mCroppedAnimationController = new CroppedAnimationController( - AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration), - new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0)); - animator.addUpdateListener(mCroppedAnimationController); - } - - private void clearState() { - mCurrentAnimation = null; - mTaggedAnimatorSetBuilder = null; - if (mDragPauseDetector != null) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_CANCEL_STATE); - } - mDragPauseDetector = null; - - if (mQuickOverviewAnimation != null) { - mQuickOverviewAnimation.cancel(); - mQuickOverviewAnimation = null; - } - mCroppedAnimationController = null; - mAnimatingToOverview = false; - - mDetector.finishedScrolling(); - } - - /** - * {@link AnimatorUpdateListener} which controls another animation for a fraction of range - */ - private static class CroppedAnimationController implements AnimatorUpdateListener { - - private final AnimatorPlaybackController mTarget; - private final FloatRange mRange; - - CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) { - mTarget = target; - mRange = range; - } - - - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - float fraction = valueAnimator.getAnimatedFraction(); - - if (mRange.start < mRange.end) { - if (fraction <= mRange.start) { - mTarget.setPlayFraction(0); - } else if (fraction >= mRange.end) { - mTarget.setPlayFraction(1); - } else { - mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start)); - } - } else if (mRange.start > mRange.end) { - if (fraction >= mRange.start) { - mTarget.setPlayFraction(0); - } else if (fraction <= mRange.end) { - mTarget.setPlayFraction(1); - } else { - mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start)); - } - } else { - // mRange.start == mRange.end - mTarget.setPlayFraction(0); - } - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 9051cfbfc2..846e80377e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -16,16 +16,19 @@ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.Utilities.getPrefs; +import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED; +import android.content.Context; +import android.content.SharedPreferences; import android.view.View; import android.view.View.AccessibilityDelegate; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.R; import com.android.launcher3.util.TouchController; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.RecentsModel; @@ -34,15 +37,25 @@ import com.android.quickstep.views.RecentsView; public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { - if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) { + SharedPreferences prefs = getPrefs(launcher); + boolean swipeUpEnabled = prefs.getBoolean(KEY_SWIPE_UP_ENABLED, true); + if (!swipeUpEnabled) { return new TouchController[] { - new EdgeSwipeController(launcher), - new TwoStepSwipeController(launcher), - new OverviewSwipeController(launcher)}; + launcher.getDragController(), + new LandscapeStatesTouchController(launcher), + new TaskViewTouchController(launcher)}; + } + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + return new TouchController[] { + launcher.getDragController(), + new LandscapeStatesTouchController(launcher), + new LandscapeEdgeSwipeController(launcher), + new TaskViewTouchController(launcher)}; } else { return new TouchController[] { - new TwoStepSwipeController(launcher), - new OverviewSwipeController(launcher)}; + launcher.getDragController(), + new PortraitStatesTouchController(launcher), + new TaskViewTouchController(launcher)}; } } @@ -57,21 +70,15 @@ public class UiFactory { } public static void onLauncherStateOrFocusChanged(Launcher launcher) { - boolean shouldBackButtonBeVisible = launcher == null - || !launcher.isInState(NORMAL) - || !launcher.hasWindowFocus(); - if (!shouldBackButtonBeVisible) { + boolean shouldBackButtonBeHidden = launcher != null + && launcher.getStateManager().getState().hideBackButton + && launcher.hasWindowFocus(); + if (shouldBackButtonBeHidden) { // Show the back button if there is a floating view visible. - DragLayer dragLayer = launcher.getDragLayer(); - for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { - View child = dragLayer.getChildAt(i); - if (child instanceof AbstractFloatingView) { - shouldBackButtonBeVisible = true; - break; - } - } + shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenView(launcher) == null; } - OverviewInteractionState.setBackButtonVisible(launcher, shouldBackButtonBeVisible); + OverviewInteractionState.getInstance(launcher) + .setBackButtonVisible(!shouldBackButtonBeHidden); } public static void resetOverview(Launcher launcher) { @@ -79,17 +86,24 @@ public class UiFactory { recents.reset(); } - public static void onStart(Launcher launcher) { - RecentsModel model = RecentsModel.getInstance(launcher); + public static void onStart(Context context) { + RecentsModel model = RecentsModel.getInstance(context); if (model != null) { model.onStart(); } } - public static void onTrimMemory(Launcher launcher, int level) { - RecentsModel model = RecentsModel.getInstance(launcher); + public static void onTrimMemory(Context context, int level) { + RecentsModel model = RecentsModel.getInstance(context); if (model != null) { model.onTrimMemory(level); } } + + public static View[] getHotseatExtraContent(Hotseat hotseat) { + return new View[] { + hotseat.findViewById(R.id.drag_indicator), + hotseat.findViewById(R.id.search_container_hotseat), + }; + } } diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java new file mode 100644 index 0000000000..9e2e5acf5f --- /dev/null +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import static com.android.launcher3.LauncherState.FAST_OVERVIEW; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.ActivityOptions; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.view.View; + +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherInitListener; +import com.android.launcher3.LauncherState; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.quickstep.views.LauncherLayoutListener; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.AssistDataReceiver; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; + +import java.util.function.BiPredicate; + +/** + * Utility class which abstracts out the logical differences between Launcher and RecentsActivity. + */ +public interface ActivityControlHelper { + + LayoutListener createLayoutListener(T activity); + + void onQuickstepGestureStarted(T activity, boolean activityVisible); + + void onQuickInteractionStart(T activity, boolean activityVisible); + + void executeOnNextDraw(T activity, TaskView targetView, Runnable action); + + void onTransitionCancelled(T activity, boolean activityVisible); + + int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect); + + void onSwipeUpComplete(T activity); + + void prepareRecentsUI(T activity, boolean activityVisible); + + AnimatorPlaybackController createControllerForVisibleActivity(T activity); + + AnimatorPlaybackController createControllerForHiddenActivity(T activity, int transitionLength); + + ActivityInitListener createActivityInitListener(BiPredicate onInitListener); + + void startRecents(Context context, Intent intent, AssistDataReceiver assistDataReceiver, + RecentsAnimationListener remoteAnimationListener); + + @UiThread + @Nullable + RecentsView getVisibleRecentsView(); + + @UiThread + boolean switchToRecentsIfVisible(); + + class LauncherActivityControllerHelper implements ActivityControlHelper { + + @Override + public LayoutListener createLayoutListener(Launcher activity) { + return new LauncherLayoutListener(activity); + } + + @Override + public void onQuickstepGestureStarted(Launcher activity, boolean activityVisible) { + activity.onQuickstepGestureStarted(activityVisible); + } + + @Override + public void onQuickInteractionStart(Launcher activity, boolean activityVisible) { + activity.getStateManager().goToState(FAST_OVERVIEW, activityVisible); + } + + @Override + public void executeOnNextDraw(Launcher activity, TaskView targetView, Runnable action) { + ViewOnDrawExecutor executor = new ViewOnDrawExecutor() { + @Override + public void onViewDetachedFromWindow(View v) { + if (!isCompleted()) { + runAllTasks(); + } + } + }; + executor.attachTo(activity, targetView, false /* waitForLoadAnimation */); + executor.execute(action); + } + + @Override + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { + RecentsView.getPageRect(dp, context, outRect); + if (dp.isVerticalBarLayout()) { + Rect targetInsets = dp.getInsets(); + int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; + return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; + } else { + return dp.heightPx - outRect.bottom; + } + } + + @Override + public void onTransitionCancelled(Launcher activity, boolean activityVisible) { + LauncherState startState = activity.getStateManager().getRestState(); + activity.getStateManager().goToState(startState, activityVisible); + } + + @Override + public void onSwipeUpComplete(Launcher activity) { + // Re apply state in case we did something funky during the transition. + activity.getStateManager().reapplyState(); + } + + @Override + public void prepareRecentsUI(Launcher activity, boolean activityVisible) { + LauncherState startState = activity.getStateManager().getState(); + if (startState.disableRestore) { + startState = activity.getStateManager().getRestState(); + } + activity.getStateManager().setRestState(startState); + + if (!activityVisible) { + // Since the launcher is not visible, we can safely reset the scroll position. + // This ensures then the next swipe up to all-apps starts from scroll 0. + activity.getAppsView().reset(false /* animate */); + activity.getStateManager().goToState(OVERVIEW, false); + + // Optimization, hide the all apps view to prevent layout while initializing + activity.getAppsView().getContentView().setVisibility(View.GONE); + } + } + + @Override + public AnimatorPlaybackController createControllerForVisibleActivity(Launcher activity) { + DeviceProfile dp = activity.getDeviceProfile(); + long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); + return activity.getStateManager().createAnimationToNewWorkspace(OVERVIEW, accuracy); + } + + @Override + public AnimatorPlaybackController createControllerForHiddenActivity( + Launcher activity, int transitionLength) { + AllAppsTransitionController controller = activity.getAllAppsController(); + AnimatorSet anim = new AnimatorSet(); + if (activity.getDeviceProfile().isVerticalBarLayout()) { + // TODO: + } else { + float scrollRange = Math.max(controller.getShiftRange(), 1); + float progressDelta = (transitionLength / scrollRange); + + float endProgress = OVERVIEW.getVerticalProgress(activity); + float startProgress = endProgress + progressDelta; + ObjectAnimator shiftAnim = ObjectAnimator.ofFloat( + controller, ALL_APPS_PROGRESS, startProgress, endProgress); + shiftAnim.setInterpolator(LINEAR); + anim.play(shiftAnim); + } + + // TODO: Link this animation to state animation, so that it is cancelled + // automatically on state change + anim.setDuration(transitionLength * 2); + return AnimatorPlaybackController.wrap(anim, transitionLength * 2); + } + + @Override + public ActivityInitListener createActivityInitListener( + BiPredicate onInitListener) { + return new LauncherInitListener(onInitListener); + } + + @Override + public void startRecents(Context context, Intent intent, + AssistDataReceiver assistDataReceiver, + RecentsAnimationListener remoteAnimationListener) { + ActivityManagerWrapper.getInstance().startRecentsActivity( + intent, assistDataReceiver, remoteAnimationListener, null, null); + } + + @Nullable + @UiThread + private Launcher getVisibleLaucher() { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app == null) { + return null; + } + Launcher launcher = (Launcher) app.getModel().getCallback(); + return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ? + launcher : null; + } + + @Nullable + @Override + public RecentsView getVisibleRecentsView() { + Launcher launcher = getVisibleLaucher(); + return launcher != null && launcher.isInState(OVERVIEW) + ? launcher.getOverviewPanel() : null; + } + + @Override + public boolean switchToRecentsIfVisible() { + Launcher launcher = getVisibleLaucher(); + if (launcher != null) { + launcher.getStateManager().goToState(OVERVIEW); + return true; + } + return false; + } + } + + class FallbackActivityControllerHelper implements ActivityControlHelper { + + @Override + public void onQuickstepGestureStarted(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public void onQuickInteractionStart(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public void executeOnNextDraw(RecentsActivity activity, TaskView targetView, + Runnable action) { + // TODO: + new Handler(Looper.getMainLooper()).post(action); + } + + @Override + public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { + FallbackRecentsView.getCenterPageRect(dp, context, outRect); + if (dp.isVerticalBarLayout()) { + Rect targetInsets = dp.getInsets(); + int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; + return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; + } else { + return dp.heightPx - outRect.bottom; + } + } + + @Override + public void onSwipeUpComplete(RecentsActivity activity) { + // TODO: + } + + @Override + public void prepareRecentsUI(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public AnimatorPlaybackController createControllerForVisibleActivity( + RecentsActivity activity) { + DeviceProfile dp = activity.getDeviceProfile(); + return createControllerForHiddenActivity(activity, Math.max(dp.widthPx, dp.heightPx)); + } + + @Override + public AnimatorPlaybackController createControllerForHiddenActivity( + RecentsActivity activity, int transitionLength) { + // We do not animate anything. Create a empty controller + AnimatorSet anim = new AnimatorSet(); + return AnimatorPlaybackController.wrap(anim, transitionLength * 2); + } + + @Override + public LayoutListener createLayoutListener(RecentsActivity activity) { + // We do not change anything as part of layout changes in fallback activity. Return a + // default layout listener. + return new LayoutListener() { + @Override + public void open() { } + + @Override + public void setHandler(WindowTransformSwipeHandler handler) { } + + @Override + public void finish() { } + }; + } + + @Override + public ActivityInitListener createActivityInitListener( + BiPredicate onInitListener) { + return new RecentsActivityTracker(onInitListener); + } + + @Override + public void startRecents(Context context, Intent intent, + AssistDataReceiver assistDataReceiver, + final RecentsAnimationListener remoteAnimationListener) { + ActivityOptions options = + ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( + new FallbackActivityOptions(remoteAnimationListener), 10000, 10000)); + context.startActivity(intent, options.toBundle()); + } + + @Nullable + @Override + public RecentsView getVisibleRecentsView() { + RecentsActivity activity = RecentsActivityTracker.getCurrentActivity(); + if (activity != null && activity.hasWindowFocus()) { + return activity.getOverviewPanel(); + } + return null; + } + + @Override + public boolean switchToRecentsIfVisible() { + return false; + } + } + + interface LayoutListener { + + void open(); + + void setHandler(WindowTransformSwipeHandler handler); + + void finish(); + } + + interface ActivityInitListener { + + void register(); + + void unregister(); + } +} diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java deleted file mode 100644 index 5871a6d59f..0000000000 --- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.quickstep; - -import android.support.annotation.WorkerThread; - -import com.android.launcher3.states.InternalStateHandler; -import com.android.quickstep.TouchConsumer.InteractionType; - -public abstract class BaseSwipeInteractionHandler extends InternalStateHandler { - - protected Runnable mGestureEndCallback; - protected boolean mIsGoingToHome; - - public void setGestureEndCallback(Runnable gestureEndCallback) { - mGestureEndCallback = gestureEndCallback; - } - - public void reset() {} - - @WorkerThread - public abstract void onGestureStarted(); - - @WorkerThread - public abstract void onGestureEnded(float endVelocity); - - public abstract void updateInteractionType(@InteractionType int interactionType); - - @WorkerThread - public abstract void onQuickScrubEnd(); - - @WorkerThread - public abstract void onQuickScrubProgress(float progress); - - @WorkerThread - public abstract void updateDisplacement(float displacement); -} diff --git a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java new file mode 100644 index 0000000000..3a7fb2db6d --- /dev/null +++ b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.graphics.Rect; +import android.util.Log; + +import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.WindowManagerWrapper; + +/** + * Temporary class to create activity options to emulate recents transition for fallback activtiy. + */ +public class FallbackActivityOptions implements RemoteAnimationRunnerCompat { + + private final RecentsAnimationListener mListener; + + public FallbackActivityOptions(RecentsAnimationListener listener) { + mListener = listener; + } + + @Override + public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, + Runnable runnable) { + DummyRecentsAnimationControllerCompat dummyRecentsAnim = + new DummyRecentsAnimationControllerCompat(runnable); + + Rect insets = new Rect(); + WindowManagerWrapper.getInstance().getStableInsets(insets); + mListener.onAnimationStart(dummyRecentsAnim, targetCompats, insets, null); + } + + @Override + public void onAnimationCancelled() { + mListener.onAnimationCanceled(); + } + + private static class DummyRecentsAnimationControllerCompat + extends RecentsAnimationControllerCompat { + + final Runnable mFinishCallback; + + public DummyRecentsAnimationControllerCompat(Runnable finishCallback) { + mFinishCallback = finishCallback; + } + + @Override + public ThumbnailData screenshotTask(int taskId) { + return new ThumbnailData(); + } + + @Override + public void setInputConsumerEnabled(boolean enabled) { } + + @Override + public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { } + + @Override + public void finish(boolean toHome) { + if (toHome) { + mFinishCallback.run(); + } + } + } +} diff --git a/quickstep/src/com/android/quickstep/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/FallbackRecentsView.java index 22f6e0cc25..032d753a68 100644 --- a/quickstep/src/com/android/quickstep/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/FallbackRecentsView.java @@ -48,10 +48,21 @@ public class FallbackRecentsView extends RecentsView implements setPadding(padding.left, padding.top, padding.right, padding.bottom); } - public static void verticalCenter(Rect padding, DeviceProfile dp) { + private static void verticalCenter(Rect padding, DeviceProfile dp) { Rect insets = dp.getInsets(); int totalSpace = (padding.top + padding.bottom - insets.top - insets.bottom) / 2; padding.top = insets.top + totalSpace; padding.bottom = insets.bottom + totalSpace; } + + public static void getCenterPageRect(DeviceProfile grid, Context context, Rect outRect) { + Rect targetPadding = getPadding(grid, context); + verticalCenter(targetPadding, grid); + Rect insets = grid.getInsets(); + outRect.set( + targetPadding.left + insets.left, + targetPadding.top + insets.top, + grid.widthPx - targetPadding.right - insets.right, + grid.heightPx - targetPadding.bottom - insets.bottom); + } } diff --git a/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java new file mode 100644 index 0000000000..f5e1f6ec56 --- /dev/null +++ b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.content.pm.LauncherApps; +import android.content.pm.ResolveInfo; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.os.Build; +import android.provider.SearchIndexablesContract.XmlResource; +import android.provider.SearchIndexablesProvider; +import android.util.Xml; + +import com.android.launcher3.R; +import com.android.launcher3.graphics.IconShapeOverride; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; +import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS; +import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS; + +@TargetApi(Build.VERSION_CODES.O) +public class LauncherSearchIndexablesProvider extends SearchIndexablesProvider { + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor queryXmlResources(String[] strings) { + MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS); + ResolveInfo settingsActivity = getContext().getPackageManager().resolveActivity( + new Intent(Intent.ACTION_APPLICATION_PREFERENCES) + .setPackage(getContext().getPackageName()), 0); + cursor.newRow() + .add(XmlResource.COLUMN_XML_RESID, R.xml.indexable_launcher_prefs) + .add(XmlResource.COLUMN_INTENT_ACTION, Intent.ACTION_APPLICATION_PREFERENCES) + .add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, getContext().getPackageName()) + .add(XmlResource.COLUMN_INTENT_TARGET_CLASS, settingsActivity.activityInfo.name); + return cursor; + } + + @Override + public Cursor queryRawData(String[] projection) { + return new MatrixCursor(INDEXABLES_RAW_COLUMNS); + } + + @Override + public Cursor queryNonIndexableKeys(String[] projection) { + MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); + if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) { + // We are not the current launcher. Hide all preferences + try (XmlResourceParser parser = getContext().getResources() + .getXml(R.xml.indexable_launcher_prefs)) { + final int depth = parser.getDepth(); + final int[] attrs = new int[] { android.R.attr.key }; + int type; + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG) { + TypedArray a = getContext().obtainStyledAttributes( + Xml.asAttributeSet(parser), attrs); + cursor.addRow(new String[] {a.getString(0)}); + a.recycle(); + } + } + } catch (IOException |XmlPullParserException e) { + throw new RuntimeException(e); + } + } else if (!IconShapeOverride.isSupported(getContext())) { + cursor.addRow(new String[] {IconShapeOverride.KEY_PREFERENCE}); + } + return cursor; + } +} diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java index 94b6faa39e..8e6e4c70b8 100644 --- a/quickstep/src/com/android/quickstep/MotionEventQueue.java +++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java @@ -19,9 +19,7 @@ import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_MASK; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT; - import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB; -import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH; import android.annotation.TargetApi; import android.os.Build; @@ -43,17 +41,17 @@ public class MotionEventQueue { private static final int ACTION_VIRTUAL = ACTION_MASK - 1; - private static final int ACTION_QUICK_SWITCH = - ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_QUICK_SCRUB_START = - ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT); + ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_QUICK_SCRUB_PROGRESS = - ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT); + ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_QUICK_SCRUB_END = - ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT); + ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_RESET = - ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT); + ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_DEFER_INIT = + ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT); + private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB = ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT); private final EventArray mEmptyArray = new EventArray(); @@ -143,9 +141,6 @@ public class MotionEventQueue { MotionEvent event = array.get(i); if (event.getActionMasked() == ACTION_VIRTUAL) { switch (event.getAction()) { - case ACTION_QUICK_SWITCH: - mConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH); - break; case ACTION_QUICK_SCRUB_START: mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB); break; @@ -161,6 +156,10 @@ public class MotionEventQueue { case ACTION_DEFER_INIT: mConsumer.deferInit(); break; + case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB: + mConsumer.onShowOverviewFromAltTab(); + mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB); + break; default: Log.e(TAG, "Invalid virtual event: " + event.getAction()); } @@ -189,14 +188,14 @@ public class MotionEventQueue { queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0)); } - public void onQuickSwitch() { - queueVirtualAction(ACTION_QUICK_SWITCH, 0); - } - public void onQuickScrubStart() { queueVirtualAction(ACTION_QUICK_SCRUB_START, 0); } + public void onOverviewShownFromAltTab() { + queueVirtualAction(ACTION_SHOW_OVERVIEW_FROM_ALT_TAB, 0); + } + public void onQuickScrubProgress(float progress) { queueVirtualAction(ACTION_QUICK_SCRUB_PROGRESS, progress); } diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java deleted file mode 100644 index 89c9d1611d..0000000000 --- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.quickstep; - - -import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; -import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH; -import static com.android.quickstep.TouchConsumer.isInteractionQuick; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.animation.RectEvaluator; -import android.annotation.TargetApi; -import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityOptions; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.os.Build; -import android.support.annotation.UiThread; -import android.view.View; -import android.view.ViewTreeObserver.OnPreDrawListener; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Hotseat; -import com.android.launcher3.Launcher; -import com.android.launcher3.Launcher.OnResumeCallback; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherState; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsTransitionController; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.TraceHelper; -import com.android.quickstep.TouchConsumer.InteractionType; -import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; -import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.WindowManagerWrapper; - -@TargetApi(Build.VERSION_CODES.O) -public class NavBarSwipeInteractionHandler extends BaseSwipeInteractionHandler implements - OnResumeCallback { - - private static final int STATE_LAUNCHER_READY = 1 << 0; - private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 4; - private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 5; - private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 6; - - private static final long MAX_SWIPE_DURATION = 200; - private static final long MIN_SWIPE_DURATION = 80; - - // Ideal velocity for a smooth transition - private static final float PIXEL_PER_MS = 2f; - - private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f; - - private final Rect mStableInsets = new Rect(); - private final Rect mSourceRect = new Rect(); - private final Rect mTargetRect = new Rect(); - private final Rect mCurrentRect = new Rect(); - private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect); - - // Shift in the range of [0, 1]. - // 0 => preview snapShot is completely visible, and hotseat is completely translated down - // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely - // visible. - private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); - - // Activity multiplier in the range of [0, 1]. When the activity becomes visible, this is - // animated to 1, so allow for a smooth transition. - private final AnimatedFloat mActivityMultiplier = new AnimatedFloat(this::updateFinalShift); - - private final int mRunningTaskId; - private final Context mContext; - - private final MultiStateCallback mStateCallback; - - private Launcher mLauncher; - private SnapshotDragView mDragView; - private RecentsView mRecentsView; - private QuickScrubController mQuickScrubController; - private Hotseat mHotseat; - - private boolean mWasLauncherAlreadyVisible; - - private boolean mLauncherReady; - private boolean mTouchEndHandled; - private float mCurrentDisplacement; - - private @InteractionType int mInteractionType; - private boolean mStartedQuickScrubFromHome; - - private Bitmap mTaskSnapshot; - - NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context, - @InteractionType int interactionType) { - mContext = context; - mInteractionType = interactionType; - mRunningTaskId = runningTaskInfo.id; - WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); - - DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext); - // TODO: If in multi window mode, dp = dp.getMultiWindowProfile() - dp = dp.copy(mContext); - // TODO: Use different insets for multi-window mode - dp.updateInsets(mStableInsets); - RecentsView.getPageRect(dp, mContext, mTargetRect); - mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right, - dp.heightPx - mStableInsets.top - mStableInsets.bottom); - - // Build the state callback - mStateCallback = new MultiStateCallback(); - mStateCallback.addCallback(STATE_LAUNCHER_READY, this::onLauncherReady); - mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_APP, this::resumeLastTask); - mStateCallback.addCallback( - STATE_SCALED_SNAPSHOT_RECENTS | STATE_ACTIVITY_MULTIPLIER_COMPLETE, - this::onAnimationToLauncherComplete); - mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_APP, - this::cleanupLauncher); - } - - private void onLauncherReady() { - mLauncherReady = true; - executeFrameUpdate(); - - long duration = Math.min(MAX_SWIPE_DURATION, - Math.max((long) (-mCurrentDisplacement / PIXEL_PER_MS), MIN_SWIPE_DURATION)); - if (mCurrentShift.getCurrentAnimation() != null) { - ObjectAnimator anim = mCurrentShift.getCurrentAnimation(); - long theirDuration = anim.getDuration() - anim.getCurrentPlayTime(); - - // TODO: Find a better heuristic - duration = (duration + theirDuration) / 2; - } - ObjectAnimator anim = mActivityMultiplier.animateToValue(1) - .setDuration(duration); - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE); - } - }); - anim.start(); - } - - public void setTaskSnapshot(Bitmap taskSnapshot) { - mTaskSnapshot = taskSnapshot; - } - - @Override - public void onLauncherResume() { - TraceHelper.partitionSection("TouchInt", "Launcher On resume"); - mDragView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mDragView.getViewTreeObserver().removeOnPreDrawListener(this); - mStateCallback.setState(STATE_LAUNCHER_READY); - TraceHelper.partitionSection("TouchInt", "Launcher drawn"); - return true; - } - }); - } - - @Override - protected boolean init(Launcher launcher, boolean alreadyOnHome) { - launcher.setOnResumeCallback(this); - mLauncher = launcher; - mRecentsView = launcher.getOverviewPanel(); - mRecentsView.showTask(mRunningTaskId); - mHotseat = mLauncher.getHotseat(); - mWasLauncherAlreadyVisible = alreadyOnHome; - - AbstractFloatingView.closeAllOpenViews(mLauncher, alreadyOnHome); - mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome); - - mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot); - mLauncher.getDragLayer().addView(mDragView); - mDragView.setPivotX(0); - mDragView.setPivotY(0); - - if (isInteractionQuick(mInteractionType)) { - updateUiForQuickScrub(); - } - - // Optimization - if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { - // All-apps search box is visible in vertical bar layout. - mLauncher.getAppsView().setVisibility(View.GONE); - } - TraceHelper.partitionSection("TouchInt", "Launcher on new intent"); - return false; - } - - public void updateInteractionType(@InteractionType int interactionType) { - Preconditions.assertUIThread(); - if (mInteractionType != INTERACTION_NORMAL) { - throw new IllegalArgumentException( - "Can't change interaction type from " + mInteractionType); - } - if (!isInteractionQuick(interactionType)) { - throw new IllegalArgumentException( - "Can't change interaction type to " + interactionType); - } - mInteractionType = interactionType; - - if (mLauncher != null) { - updateUiForQuickScrub(); - } - } - - private void updateUiForQuickScrub() { - mStartedQuickScrubFromHome = mWasLauncherAlreadyVisible; - mQuickScrubController = mRecentsView.getQuickScrubController(); - mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome); - animateToProgress(1f, MAX_SWIPE_DURATION); - if (mStartedQuickScrubFromHome) { - mDragView.setVisibility(View.INVISIBLE); - } - } - - @UiThread - public void updateDisplacement(float displacement) { - mCurrentDisplacement = displacement; - executeFrameUpdate(); - } - - private void executeFrameUpdate() { - if (mLauncherReady) { - final float displacement = -mCurrentDisplacement; - int hotseatSize = getHotseatSize(); - float translation = Utilities.boundToRange(displacement, 0, hotseatSize); - float shift = hotseatSize == 0 ? 0 : translation / hotseatSize; - mCurrentShift.updateValue(shift); - } - } - - @UiThread - private void updateFinalShift() { - if (!mLauncherReady || mStartedQuickScrubFromHome) { - return; - } - - float shift = mCurrentShift.value * mActivityMultiplier.value; - - AllAppsTransitionController controller = mLauncher.getAllAppsController(); - float range = getHotseatSize() / controller.getShiftRange(); - controller.setProgress(1 + (1 - shift) * range); - - mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); - - float scale = (float) mCurrentRect.width() / mSourceRect.width(); - mDragView.setTranslationX(mCurrentRect.left - mStableInsets.left * scale * shift); - mDragView.setTranslationY(mCurrentRect.top - mStableInsets.top * scale * shift); - mDragView.setScaleX(scale); - mDragView.setScaleY(scale); - // TODO: mDragView.getViewBounds().setClipLeft((int) (mStableInsets.left * shift)); - mDragView.getViewBounds().setClipTop((int) (mStableInsets.top * shift)); - // TODO: mDragView.getViewBounds().setClipRight((int) (mStableInsets.right * shift)); - mDragView.getViewBounds().setClipBottom((int) (mStableInsets.bottom * shift)); - } - - private int getHotseatSize() { - return mLauncher.getDeviceProfile().isVerticalBarLayout() - ? mHotseat.getWidth() : mHotseat.getHeight(); - } - - @Override - public void onGestureStarted() { } - - @UiThread - public void onGestureEnded(float endVelocity) { - if (mTouchEndHandled) { - return; - } - mTouchEndHandled = true; - - Resources res = mContext.getResources(); - float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity); - boolean isFling = Math.abs(endVelocity) > flingThreshold; - - long duration = MAX_SWIPE_DURATION; - final float endShift; - if (!isFling) { - endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0; - } else { - endShift = endVelocity < 0 ? 1 : 0; - float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity); - if (Math.abs(endVelocity) > minFlingVelocity && mLauncherReady) { - float distanceToTravel = (endShift - mCurrentShift.value) * getHotseatSize(); - - // we want the page's snap velocity to approximately match the velocity at - // which the user flings, so we scale the duration by a value near to the - // derivative of the scroll interpolator at zero, ie. 5. - duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity)); - } - } - - animateToProgress(endShift, duration); - } - - /** Animates to the given progress, where 0 is the current app and 1 is overview. */ - private void animateToProgress(float progress, long duration) { - mIsGoingToHome = Float.compare(progress, 1) == 0; - ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration); - anim.setInterpolator(Interpolators.SCROLL); - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mStateCallback.setState(mIsGoingToHome - ? STATE_SCALED_SNAPSHOT_RECENTS : STATE_SCALED_SNAPSHOT_APP); - } - }); - anim.start(); - } - - @UiThread - private void resumeLastTask() { - RecentsTaskLoadPlan loadPlan = RecentsModel.getInstance(mContext).getLastLoadPlan(); - if (loadPlan != null) { - Task task = loadPlan.getTaskStack().findTaskWithId(mRunningTaskId); - if (task != null) { - ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 0, 0); - ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts, - null, null); - } - } - } - - public void reset() { - mCurrentShift.cancelAnimation(); - if (mGestureEndCallback != null) { - mGestureEndCallback.run(); - } - } - - private void cleanupLauncher() { - reset(); - - // TODO: These should be done as part of ActivityOptions#OnAnimationStarted - mLauncher.getStateManager().reapplyState(); - mLauncher.setOnResumeCallback(() -> mDragView.close(false)); - } - - private void onAnimationToLauncherComplete() { - reset(); - - mDragView.close(false); - View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage()); - if (currentRecentsPage instanceof TaskView) { - ((TaskView) currentRecentsPage).animateIconToScale(1f); - } - if (mInteractionType == INTERACTION_QUICK_SWITCH) { - if (mQuickScrubController != null) { - mQuickScrubController.onQuickSwitch(); - } - } - } - - public void onQuickScrubEnd() { - if (mQuickScrubController != null) { - mQuickScrubController.onQuickScrubEnd(); - } else { - // TODO: - } - } - - public void onQuickScrubProgress(float progress) { - if (mQuickScrubController != null) { - mQuickScrubController.onQuickScrubProgress(progress); - } else { - // TODO: - } - } -} diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index c96f6d73aa..4877abbe19 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -21,28 +21,20 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; - -import static com.android.quickstep.RemoteRunnable.executeSafely; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityOptions; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Color; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Looper; -import android.util.Log; +import android.os.SystemClock; import android.view.Choreographer; import android.view.Display; import android.view.MotionEvent; @@ -52,9 +44,7 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import com.android.launcher3.MainThreadExecutor; -import com.android.launcher3.Utilities; import com.android.launcher3.util.TraceHelper; -import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.AssistDataReceiver; import com.android.systemui.shared.system.BackgroundExecutor; @@ -73,16 +63,15 @@ import java.util.concurrent.TimeUnit; */ @TargetApi(Build.VERSION_CODES.P) public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer { - private static final String TAG = "ActivityTouchConsumer"; private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150; - private static final int[] DEFERRED_HIT_TARGETS = DEBUG_SHOW_OVERVIEW_BUTTON + private static final int[] DEFERRED_HIT_TARGETS = false ? new int[] {HIT_TARGET_BACK, HIT_TARGET_OVERVIEW} : new int[] {HIT_TARGET_BACK}; private final RunningTaskInfo mRunningTask; private final RecentsModel mRecentsModel; private final Intent mHomeIntent; - private final ISystemUiProxy mISystemUiProxy; + private final ActivityControlHelper mActivityControlHelper; private final MainThreadExecutor mMainThreadExecutor; private final Choreographer mBackgroundThreadChoreographer; @@ -93,7 +82,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private boolean mTouchThresholdCrossed; private int mTouchSlop; private float mStartDisplacement; - private BaseSwipeInteractionHandler mInteractionHandler; + private WindowTransformSwipeHandler mInteractionHandler; private int mDisplayRotation; private Rect mStableInsets = new Rect(); @@ -102,7 +91,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private boolean mIsGoingToHome; public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo, - RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy, + RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer, @HitTarget int downHitTarget, VelocityTracker velocityTracker) { super(base); @@ -110,12 +99,17 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC mRecentsModel = recentsModel; mHomeIntent = homeIntent; mVelocityTracker = velocityTracker; - mISystemUiProxy = systemUiProxy; + mActivityControlHelper = activityControl; mMainThreadExecutor = mainThreadExecutor; mBackgroundThreadChoreographer = backgroundThreadChoreographer; mIsDeferredDownTarget = Arrays.binarySearch(DEFERRED_HIT_TARGETS, downHitTarget) >= 0; } + @Override + public void onShowOverviewFromAltTab() { + startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis()); + } + @Override public void accept(MotionEvent ev) { if (mVelocityTracker == null) { @@ -132,7 +126,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC // Start the window animation on down to give more time for launcher to draw if the // user didn't start the gesture over the back button - if (!isUsingScreenShot() && !mIsDeferredDownTarget) { + if (!mIsDeferredDownTarget) { startTouchTrackingForWindowAnimation(ev.getEventTime()); } @@ -172,14 +166,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC if (mTouchThresholdCrossed) { mStartDisplacement = Math.signum(displacement) * mTouchSlop; - if (isUsingScreenShot()) { - startTouchTrackingForScreenshotAnimation(); - } else if (mIsDeferredDownTarget) { + if (mIsDeferredDownTarget) { // If we deferred starting the window animation on touch down, then // start tracking now startTouchTrackingForWindowAnimation(ev.getEventTime()); } - notifyGestureStarted(); } } else if (mInteractionHandler != null) { @@ -205,11 +196,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } // Notify the handler that the gesture has actually started mInteractionHandler.onGestureStarted(); - - // Notify the system that we have started tracking the event - if (mISystemUiProxy != null) { - executeSafely(mISystemUiProxy::onRecentsAnimationStarted); - } } private boolean isNavBarOnRight() { @@ -220,69 +206,10 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0; } - private boolean isUsingScreenShot() { - return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_for_swipe_up", false); - } - - /** - * Called when the gesture has started. - */ - private void startTouchTrackingForScreenshotAnimation() { - // Create the shared handler - final NavBarSwipeInteractionHandler handler = - new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL); - - TraceHelper.partitionSection("TouchInt", "Thershold crossed "); - - // Start the recents activity on a background thread - BackgroundExecutor.get().submit(() -> { - // Get the snap shot before - handler.setTaskSnapshot(getCurrentTaskSnapshot()); - - // Start the launcher activity with our custom handler - Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent)); - startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle()); - TraceHelper.partitionSection("TouchInt", "Home started"); - }); - - // Preload the plan - mRecentsModel.loadTasks(mRunningTask.id, null); - mInteractionHandler = handler; - mInteractionHandler.setGestureEndCallback(mEventQueue::reset); - } - - private Bitmap getCurrentTaskSnapshot() { - TraceHelper.beginSection("TaskSnapshot"); - // TODO: We are using some hardcoded layers for now, to best approximate the activity layers - Point displaySize = new Point(); - Display display = getSystemService(WindowManager.class).getDefaultDisplay(); - display.getRealSize(displaySize); - int rotation = display.getRotation(); - // The rotation is backwards in landscape, so flip it. - if (rotation == Surface.ROTATION_270) { - rotation = Surface.ROTATION_90; - } else if (rotation == Surface.ROTATION_90) { - rotation = Surface.ROTATION_270; - } - try { - return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000, - false, rotation).toBitmap(); - } catch (Exception e) { - Log.e(TAG, "Error capturing snapshot", e); - - // Return a dummy bitmap - Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565); - bitmap.eraseColor(Color.WHITE); - return bitmap; - } finally { - TraceHelper.endSection("TaskSnapshot"); - } - } - private void startTouchTrackingForWindowAnimation(long touchTimeMs) { // Create the shared handler - final WindowTransformSwipeHandler handler = - new WindowTransformSwipeHandler(mRunningTask, this, touchTimeMs); + final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler( + mRunningTask, this, touchTimeMs, mActivityControlHelper); // Preload the plan mRecentsModel.loadTasks(mRunningTask.id, null); @@ -299,8 +226,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC handler.initWhenReady(); TraceHelper.beginSection("RecentsController"); - Runnable startActivity = () -> ActivityManagerWrapper.getInstance() - .startRecentsActivity(mHomeIntent, + Runnable startActivity = () -> mActivityControlHelper.startRecents(this, mHomeIntent, new AssistDataReceiver() { @Override public void onHandleAssistData(Bundle bundle) { @@ -329,7 +255,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC handler.onRecentsAnimationCanceled(); } } - }, null, null); + }); if (Looper.myLooper() != Looper.getMainLooper()) { startActivity.run(); @@ -358,7 +284,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId) : mVelocityTracker.getYVelocity(mActivePointerId); mInteractionHandler.onGestureEnded(velocity); - } else if (!isUsingScreenShot()) { + } else { // Since we start touch tracking on DOWN, we may reach this state without actually // starting the gesture. In that case, just cleanup immediately. reset(); @@ -375,7 +301,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC public void reset() { // Clean up the old interaction handler if (mInteractionHandler != null) { - final BaseSwipeInteractionHandler handler = mInteractionHandler; + final WindowTransformSwipeHandler handler = mInteractionHandler; mInteractionHandler = null; mIsGoingToHome = handler.mIsGoingToHome; mMainThreadExecutor.execute(handler::reset); @@ -385,24 +311,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC @Override public void updateTouchTracking(int interactionType) { notifyGestureStarted(); - - if (isUsingScreenShot()) { - mMainThreadExecutor.execute(() -> { - if (mInteractionHandler != null) { - mInteractionHandler.updateInteractionType(interactionType); - } - }); - } else { - if (mInteractionHandler != null) { - mInteractionHandler.updateInteractionType(interactionType); - } + if (mInteractionHandler != null) { + mInteractionHandler.updateInteractionType(interactionType); } } @Override public Choreographer getIntrimChoreographer(MotionEventQueue queue) { mEventQueue = queue; - return isUsingScreenShot() ? null : mBackgroundThreadChoreographer; + return mBackgroundThreadChoreographer; } @Override diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 031624aaca..311411f4cf 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -16,24 +16,55 @@ package com.android.quickstep; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RecentTaskInfo; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; import android.os.Build; +import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; +import android.support.annotation.UiThread; +import android.support.annotation.WorkerThread; +import android.util.SparseArray; import android.view.ViewConfiguration; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.states.InternalStateHandler; +import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper; +import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.RecentsTransition; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.AssistDataReceiver; +import com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; /** * Helper class to handle various atomic commands for switching between Overview. @@ -41,56 +72,263 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; @TargetApi(Build.VERSION_CODES.P) public class OverviewCommandHelper extends InternalStateHandler { - private static final boolean DEBUG_START_FALLBACK_ACTIVITY = DEBUG_SHOW_OVERVIEW_BUTTON; + private static final int RID_RESET_SWIPE_HANDLER = 0; + private static final int RID_CANCEL_CONTROLLER = 1; + private static final int RID_CANCEL_ZOOM_OUT_ANIMATION = 2; + + private static final long RECENTS_LAUNCH_DURATION = 150; + + private static final String TAG = "OverviewCommandHelper"; + private static final boolean DEBUG_START_FALLBACK_ACTIVITY = false; private final Context mContext; private final ActivityManagerWrapper mAM; + private final RecentsModel mRecentsModel; + private final MainThreadExecutor mMainThreadExecutor; public final Intent homeIntent; public final ComponentName launcher; + private final SparseArray mCurrentCommandFinishRunnables = new SparseArray<>(); + // Monotonically increasing command ids. + private int mCurrentCommandId = 0; + private long mLastToggleTime; + private WindowTransformSwipeHandler mWindowTransformSwipeHandler; + + private final Point mWindowSize = new Point(); + private final Rect mTaskTargetRect = new Rect(); + private final RectF mTempTaskTargetRect = new RectF(); public OverviewCommandHelper(Context context) { mContext = context; mAM = ActivityManagerWrapper.getInstance(); + mMainThreadExecutor = new MainThreadExecutor(); + mRecentsModel = RecentsModel.getInstance(mContext); homeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setPackage(context.getPackageName()) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent, 0); - launcher = new ComponentName(context.getPackageName(), info.activityInfo.name); + + if (DEBUG_START_FALLBACK_ACTIVITY) { + launcher = new ComponentName(context, RecentsActivity.class); + homeIntent.addCategory(Intent.CATEGORY_DEFAULT) + .removeCategory(Intent.CATEGORY_HOME); + } else { + launcher = new ComponentName(context.getPackageName(), info.activityInfo.name); + } + // Clear the packageName as system can fail to dedupe it b/64108432 homeIntent.setComponent(launcher).setPackage(null); } - public void onOverviewToggle() { - if (DEBUG_START_FALLBACK_ACTIVITY) { - mContext.startActivity(new Intent(mContext, RecentsActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); - return; - } + private void openRecents() { + Intent intent = addToIntent(new Intent(homeIntent)); + mContext.startActivity(intent); + initWhenReady(); + } - long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime; - mLastToggleTime = SystemClock.elapsedRealtime(); - - if (isOverviewAlmostVisible()) { - boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout(); - startNonLauncherTask(isQuickTap ? 2 : 1); + @UiThread + private void addFinishCommand(int requestId, int id, Runnable action) { + if (requestId < mCurrentCommandId) { + action.run(); } else { - Intent intent = addToIntent(new Intent(homeIntent)); - mContext.startActivity(intent); - initWhenReady(); + mCurrentCommandFinishRunnables.put(id, action); } } - private void startNonLauncherTask(int backStackCount) { - for (RecentTaskInfo rti : mAM.getRecentTasks(backStackCount, UserHandle.myUserId())) { - backStackCount--; - if (backStackCount == 0) { - mAM.startActivityFromRecents(rti.id, null); + @UiThread + private void clearFinishCommand(int requestId, int id) { + if (requestId == mCurrentCommandId) { + mCurrentCommandFinishRunnables.remove(id); + } + } + + @UiThread + private void initSwipeHandler(ActivityControlHelper helper, long time, + Consumer onAnimationInitCallback) { + final int commandId = mCurrentCommandId; + RunningTaskInfo taskInfo = ActivityManagerWrapper.getInstance().getRunningTask(); + final WindowTransformSwipeHandler handler = + new WindowTransformSwipeHandler(taskInfo, mContext, time, helper); + + // Preload the plan + mRecentsModel.loadTasks(taskInfo.id, null); + mWindowTransformSwipeHandler = handler; + + mTempTaskTargetRect.setEmpty(); + handler.setGestureEndCallback(() -> { + if (mWindowTransformSwipeHandler == handler) { + mWindowTransformSwipeHandler = null; + mTempTaskTargetRect.setEmpty(); } + clearFinishCommand(commandId, RID_RESET_SWIPE_HANDLER); + clearFinishCommand(commandId, RID_CANCEL_CONTROLLER); + }); + handler.initWhenReady(); + addFinishCommand(commandId, RID_RESET_SWIPE_HANDLER, handler::reset); + + TraceHelper.beginSection(TAG); + Runnable startActivity = () -> helper.startRecents(mContext, homeIntent, + new AssistDataReceiver() { + @Override + public void onHandleAssistData(Bundle bundle) { + mRecentsModel.preloadAssistData(taskInfo.id, bundle); + } + }, + new RecentsAnimationListener() { + public void onAnimationStart( + RecentsAnimationControllerCompat controller, + RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, + Rect minimizedHomeBounds) { + if (mWindowTransformSwipeHandler == handler) { + TraceHelper.partitionSection(TAG, "Received"); + handler.onRecentsAnimationStart(controller, apps, homeContentInsets, + minimizedHomeBounds); + mTempTaskTargetRect.set(handler.getTargetRect(mWindowSize)); + + mMainThreadExecutor.execute(() -> { + addFinishCommand(commandId, + RID_CANCEL_CONTROLLER, () -> controller.finish(true)); + if (commandId == mCurrentCommandId) { + onAnimationInitCallback.accept(handler); + } + }); + } else { + TraceHelper.endSection(TAG, "Finishing no handler"); + controller.finish(false /* toHome */); + } + } + + public void onAnimationCanceled() { + TraceHelper.endSection(TAG, "Cancelled: " + handler); + if (mWindowTransformSwipeHandler == handler) { + handler.onRecentsAnimationCanceled(); + } + } + }); + + // We should almost always get touch-town on background thread. This is an edge case + // when the background Choreographer has not yet initialized. + BackgroundExecutor.get().submit(startActivity); + } + + @UiThread + private void startZoomOutAnim(final WindowTransformSwipeHandler handler) { + final int commandId = mCurrentCommandId; + ValueAnimator anim = ValueAnimator.ofInt(0, -handler.getTransitionLength()); + anim.addUpdateListener((a) -> handler.updateDisplacement((Integer) a.getAnimatedValue())); + anim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + handler.onGestureEnded(0); + clearFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION); + } + }); + handler.onGestureStarted(); + anim.setDuration(RECENTS_LAUNCH_DURATION); + anim.setInterpolator(Interpolators.AGGRESSIVE_EASE); + anim.start(); + addFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION, anim::cancel); + } + + public void onOverviewToggle() { + long time = SystemClock.elapsedRealtime(); + mMainThreadExecutor.execute(() -> { + long elapsedTime = time - mLastToggleTime; + mLastToggleTime = time; + + mCurrentCommandId++; + mTempTaskTargetRect.round(mTaskTargetRect); + boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout(); + int runnableCount = mCurrentCommandFinishRunnables.size(); + if (runnableCount > 0) { + for (int i = 0; i < runnableCount; i++) { + mCurrentCommandFinishRunnables.valueAt(i).run(); + } + mCurrentCommandFinishRunnables.clear(); + isQuickTap = true; + } + + ActivityControlHelper helper = getActivityControlHelper(); + RecentsView recents = helper.getVisibleRecentsView(); + if (recents != null) { + int childCount = recents.getChildCount(); + if (childCount != 0) { + ((TaskView) recents.getChildAt(childCount >= 2 ? 1 : 0)).launchTask(true); + } + + // There are not enough tasks. Skip + return; + } + + if (isQuickTap) { + // Focus last task. Start is on background thread so that all ActivityManager calls + // are serialized + BackgroundExecutor.get().submit(this::startLastTask); + return; + } + if (helper.switchToRecentsIfVisible()) { + return; + } + + initSwipeHandler(helper, time, this::startZoomOutAnim); + }); + } + + public void onOverviewShown() { + getLauncher().runOnUiThread(() -> { + if (isOverviewAlmostVisible()) { + final RecentsView rv = getLauncher().getOverviewPanel(); + rv.snapToTaskAfterNext(); + } else { + openRecents(); + } + } + ); + } + + public void onOverviewHidden() { + getLauncher().runOnUiThread(() -> { + if (isOverviewAlmostVisible()) { + final RecentsView rv = getLauncher().getOverviewPanel(); + rv.launchNextTask(); + } + } + ); + } + + @WorkerThread + private void startLastTask() { + // TODO: This should go through recents model. + List tasks = mAM.getRecentTasks(2, UserHandle.myUserId()); + if (tasks.size() > 1) { + RecentTaskInfo rti = tasks.get(1); + + final ActivityOptions options; + if (!mTaskTargetRect.isEmpty()) { + final Rect targetRect = new Rect(mTaskTargetRect); + targetRect.offset(Utilities.isRtl(mContext.getResources()) + ? - mTaskTargetRect.width() : mTaskTargetRect.width(), 0); + final AppTransitionAnimationSpecCompat specCompat = + new AppTransitionAnimationSpecCompat(rti.id, null, targetRect); + AppTransitionAnimationSpecsFuture specFuture = + new AppTransitionAnimationSpecsFuture(mMainThreadExecutor.getHandler()) { + + @Override + public List composeSpecs() { + return Collections.singletonList(specCompat); + } + }; + options = RecentsTransition.createAspectScaleAnimation(mContext, + mMainThreadExecutor.getHandler(), true /* scaleUp */, + specFuture, () -> {}); + } else { + options = ActivityOptions.makeBasic(); + } + mAM.startActivityFromRecents(rti.id, options); } } @@ -117,4 +355,15 @@ public class OverviewCommandHelper extends InternalStateHandler { return false; } + public boolean isUsingFallbackActivity() { + return DEBUG_START_FALLBACK_ACTIVITY; + } + + public ActivityControlHelper getActivityControlHelper() { + if (DEBUG_START_FALLBACK_ACTIVITY) { + return new FallbackActivityControllerHelper(); + } else { + return new LauncherActivityControllerHelper(); + } + } } diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java index 4af89bf46b..22b175779d 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -15,20 +15,28 @@ */ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; +import static com.android.launcher3.Utilities.getPrefs; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.support.annotation.WorkerThread; import android.util.Log; +import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.ISystemUiProxy; +import java.util.concurrent.ExecutionException; + /** * Sets overview interaction flags, such as: * @@ -39,55 +47,109 @@ import com.android.systemui.shared.recents.ISystemUiProxy; * * @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags. */ -public class OverviewInteractionState { +public class OverviewInteractionState implements OnSharedPreferenceChangeListener { private static final String TAG = "OverviewFlags"; - private static final Handler sUiHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - updateOverviewInteractionFlag((Context) msg.obj, msg.what, msg.arg1 == 1); - } - }; - private static final Handler sBackgroundHandler = new Handler( - UiThreadHelper.getBackgroundLooper()) { - @Override - public void handleMessage(Message msg) { - ISystemUiProxy systemUiProxy = (ISystemUiProxy) msg.obj; - int flags = msg.what; - try { - systemUiProxy.setInteractionState(flags); - } catch (RemoteException e) { - Log.w(TAG, "Unable to update overview interaction flags", e); + + // We do not need any synchronization for this variable as its only written on UI thread. + private static OverviewInteractionState INSTANCE; + + public static OverviewInteractionState getInstance(final Context context) { + if (INSTANCE == null) { + if (Looper.myLooper() == Looper.getMainLooper()) { + INSTANCE = new OverviewInteractionState(context.getApplicationContext()); + } else { + try { + return new MainThreadExecutor().submit( + () -> OverviewInteractionState.getInstance(context)).get(); + } catch (InterruptedException|ExecutionException e) { + throw new RuntimeException(e); + } } } - }; - - private static int sFlags = DEBUG_SHOW_OVERVIEW_BUTTON ? FLAG_SHOW_OVERVIEW_BUTTON : 0; - - public static void setBackButtonVisible(Context context, boolean visible) { - updateFlagOnUi(context, FLAG_HIDE_BACK_BUTTON, !visible); + return INSTANCE; } - private static void updateFlagOnUi(Context context, int flag, boolean enabled) { - sUiHandler.removeMessages(flag); - sUiHandler.sendMessage(sUiHandler.obtainMessage(flag, enabled ? 1 : 0, 0, context)); + public static final String KEY_SWIPE_UP_ENABLED = "pref_enable_quickstep"; + + private static final int MSG_SET_PROXY = 200; + private static final int MSG_SET_BACK_BUTTON_VISIBLE = 201; + private static final int MSG_SET_SWIPE_UP_ENABLED = 202; + + private final Handler mUiHandler; + private final Handler mBgHandler; + + // These are updated on the background thread + private ISystemUiProxy mISystemUiProxy; + private boolean mBackButtonVisible = true; + private boolean mSwipeUpEnabled = true; + + private OverviewInteractionState(Context context) { + mUiHandler = new Handler(this::handleUiMessage); + mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage); + + SharedPreferences prefs = getPrefs(context); + prefs.registerOnSharedPreferenceChangeListener(this); + onSharedPreferenceChanged(prefs, KEY_SWIPE_UP_ENABLED); } - private static void updateOverviewInteractionFlag(Context context, int flag, boolean enabled) { - if (enabled) { - sFlags |= flag; - } else { - sFlags &= ~flag; + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String s) { + if (KEY_SWIPE_UP_ENABLED.equals(s)) { + mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED); + boolean swipeUpEnabled = prefs.getBoolean(s, true); + mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, + swipeUpEnabled ? 1 : 0, 0).sendToTarget(); } + } - ISystemUiProxy systemUiProxy = RecentsModel.getInstance(context).getSystemUiProxy(); - if (systemUiProxy == null) { - Log.w(TAG, "Unable to update overview interaction flags; not bound to service"); + public void setBackButtonVisible(boolean visible) { + mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_VISIBLE); + mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_VISIBLE, visible ? 1 : 0, 0) + .sendToTarget(); + } + + public void setSystemUiProxy(ISystemUiProxy proxy) { + mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget(); + } + + private boolean handleUiMessage(Message msg) { + mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2).sendToTarget(); + return true; + } + + private boolean handleBgMessage(Message msg) { + switch (msg.what) { + case MSG_SET_PROXY: + mISystemUiProxy = (ISystemUiProxy) msg.obj; + break; + case MSG_SET_BACK_BUTTON_VISIBLE: + mBackButtonVisible = msg.arg1 != 0; + break; + case MSG_SET_SWIPE_UP_ENABLED: + mSwipeUpEnabled = msg.arg1 != 0; + break; + } + applyFlags(); + return true; + } + + @WorkerThread + private void applyFlags() { + if (mISystemUiProxy == null) { return; } - // If we aren't already setting these flags, do so now on the background thread. - if (!sBackgroundHandler.hasMessages(sFlags)) { - sBackgroundHandler.sendMessage(sBackgroundHandler.obtainMessage(sFlags, systemUiProxy)); + + int flags; + if (mSwipeUpEnabled) { + flags = mBackButtonVisible ? 0 : FLAG_HIDE_BACK_BUTTON; + } else { + flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON; + } + try { + mISystemUiProxy.setInteractionState(flags); + } catch (RemoteException e) { + Log.w(TAG, "Unable to update overview interaction flags", e); } } } diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java index a154b291e2..d868d122b7 100644 --- a/quickstep/src/com/android/quickstep/QuickScrubController.java +++ b/quickstep/src/com/android/quickstep/QuickScrubController.java @@ -36,7 +36,7 @@ import com.android.quickstep.views.TaskView; */ public class QuickScrubController implements OnAlarmListener { - public static final int QUICK_SWITCH_START_DURATION = 210; + public static final int QUICK_SCRUB_START_DURATION = 210; private static final boolean ENABLE_AUTO_ADVANCE = true; private static final int NUM_QUICK_SCRUB_SECTIONS = 3; @@ -53,7 +53,6 @@ public class QuickScrubController implements OnAlarmListener { private int mQuickScrubSection; private boolean mStartedFromHome; private boolean mHasAlarmRun; - private boolean mQuickSwitched; private boolean mFinishedTransitionToQuickScrub; public QuickScrubController(BaseActivity activity, RecentsView recentsView) { @@ -70,7 +69,6 @@ public class QuickScrubController implements OnAlarmListener { mStartedFromHome = startingFromHome; mQuickScrubSection = 0; mHasAlarmRun = false; - mQuickSwitched = false; mFinishedTransitionToQuickScrub = false; snapToNextTaskIfAvailable(); @@ -84,7 +82,13 @@ public class QuickScrubController implements OnAlarmListener { } int page = mRecentsView.getNextPage(); Runnable launchTaskRunnable = () -> { - ((TaskView) mRecentsView.getPageAt(page)).launchTask(true); + TaskView taskView = ((TaskView) mRecentsView.getPageAt(page)); + if (taskView != null) { + taskView.launchTask(true); + } else { + // Break out of quick scrub so user can interact with launcher. + mActivity.onBackPressed(); + } }; int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen()) * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE; @@ -119,48 +123,24 @@ public class QuickScrubController implements OnAlarmListener { } } - public void onQuickSwitch() { - mQuickSwitched = true; - quickSwitchIfReady(); - } - public void onFinishedTransitionToQuickScrub() { mFinishedTransitionToQuickScrub = true; - quickSwitchIfReady(); - } - - /** - * Immediately launches the current task (which we snapped to in onQuickScrubStart) if we've - * gotten the onQuickSwitch callback and the transition to quick scrub has completed. - */ - private void quickSwitchIfReady() { - if (mQuickSwitched && mFinishedTransitionToQuickScrub) { - onQuickScrubEnd(); - mActivity.getUserEventDispatcher().logActionOnControl(Touch.FLING, - ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ? - ContainerType.WORKSPACE : ContainerType.APP); - } } public void snapToNextTaskIfAvailable() { if (mInQuickScrub && mRecentsView.getChildCount() > 0) { int toPage = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1; - goToPageWithHaptic(toPage, QUICK_SWITCH_START_DURATION); + mRecentsView.snapToPage(toPage, QUICK_SCRUB_START_DURATION); } } private void goToPageWithHaptic(int pageToGoTo) { - goToPageWithHaptic(pageToGoTo, -1); - } - - private void goToPageWithHaptic(int pageToGoTo, int overrideDuration) { pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1); if (pageToGoTo != mRecentsView.getNextPage()) { - int duration = overrideDuration > -1 ? overrideDuration - : Math.abs(pageToGoTo - mRecentsView.getNextPage()) + int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage()) * QUICKSCRUB_SNAP_DURATION_PER_PAGE; mRecentsView.snapToPage(pageToGoTo, duration); - mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, + mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } } diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 598c34de0e..e5792056aa 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,17 +15,26 @@ */ package com.android.quickstep; +import android.app.ActivityOptions; import android.os.Bundle; +import android.view.View; -import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; +import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.uioverrides.UiFactory; +import com.android.launcher3.views.BaseDragLayer; /** * A simple activity to show the recently launched tasks */ -public class RecentsActivity extends BaseActivity { +public class RecentsActivity extends BaseDraggingActivity { + + private RecentsRootView mRecentsRootView; + private FallbackRecentsView mFallbackRecentsView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -39,5 +48,55 @@ public class RecentsActivity extends BaseActivity { : new InvariantDeviceProfile(this).getDeviceProfile(this)); setContentView(R.layout.fallback_recents_activity); + mRecentsRootView = findViewById(R.id.drag_layer); + mFallbackRecentsView = findViewById(R.id.overview_panel); + + RecentsActivityTracker.onRecentsActivityCreate(this); + } + + @Override + public BaseDragLayer getDragLayer() { + return mRecentsRootView; + } + + @Override + public View getRootView() { + return mRecentsRootView; + } + + @Override + public T getOverviewPanel() { + return (T) mFallbackRecentsView; + } + + @Override + public BadgeInfo getBadgeInfoForItem(ItemInfo info) { + return null; + } + + @Override + public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) { + return null; + } + + @Override + public void invalidateParent(ItemInfo info) { } + + @Override + protected void onStart() { + super.onStart(); + UiFactory.onStart(this); + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + UiFactory.onTrimMemory(this, level); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + RecentsActivityTracker.onRecentsActivityDestroy(this); } } diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java new file mode 100644 index 0000000000..5bd606eb4a --- /dev/null +++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.annotation.TargetApi; +import android.os.Build; + +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; + +import java.lang.ref.WeakReference; +import java.util.function.BiPredicate; + +/** + * Utility class to track create/destroy for RecentsActivity + */ +@TargetApi(Build.VERSION_CODES.P) +public class RecentsActivityTracker implements ActivityInitListener { + + private static final Object LOCK = new Object(); + private static WeakReference sTracker = new WeakReference<>(null); + private static WeakReference sCurrentActivity = new WeakReference<>(null); + + private final BiPredicate mOnInitListener; + + public RecentsActivityTracker(BiPredicate onInitListener) { + mOnInitListener = onInitListener; + } + + @Override + public void register() { + synchronized (LOCK) { + sTracker = new WeakReference<>(this); + } + } + + @Override + public void unregister() { + synchronized (LOCK) { + if (sTracker.get() == this) { + sTracker.clear(); + } + } + } + + public static void onRecentsActivityCreate(RecentsActivity activity) { + synchronized (LOCK) { + RecentsActivityTracker tracker = sTracker.get(); + if (tracker != null && tracker.mOnInitListener.test(activity, false)) { + sTracker.clear(); + } + sCurrentActivity = new WeakReference<>(activity); + } + } + + public static void onRecentsActivityDestroy(RecentsActivity activity) { + synchronized (LOCK) { + if (sCurrentActivity.get() == activity) { + sCurrentActivity.clear(); + } + } + } + + public static RecentsActivity getCurrentActivity() { + synchronized (LOCK) { + return sCurrentActivity.get(); + } + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index 392b73fc63..1e43202a1f 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.support.annotation.WorkerThread; import android.util.LruCache; import android.util.SparseArray; +import android.view.accessibility.AccessibilityManager; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; @@ -85,6 +86,7 @@ public class RecentsModel extends TaskStackChangeListener { private ISystemUiProxy mSystemUiProxy; private boolean mClearAssistCacheOnStackChange = true; private final boolean mPreloadTasksInBackground; + private final AccessibilityManager mAccessibilityManager; private RecentsModel(Context context) { mContext = context; @@ -111,6 +113,7 @@ public class RecentsModel extends TaskStackChangeListener { mTaskChangeId = 1; loadTasks(-1, null); + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } public RecentsTaskLoader getRecentsTaskLoader() { @@ -140,7 +143,7 @@ public class RecentsModel extends TaskStackChangeListener { // Preload the plan RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext); PreloadOptions opts = new PreloadOptions(); - opts.loadTitles = false; + opts.loadTitles = mAccessibilityManager.isEnabled(); loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId()); // Set the load plan on UI thread mMainThreadExecutor.execute(() -> { @@ -187,7 +190,7 @@ public class RecentsModel extends TaskStackChangeListener { launchOpts.onlyLoadPausedActivities = true; launchOpts.loadThumbnails = true; PreloadOptions preloadOpts = new PreloadOptions(); - preloadOpts.loadTitles = false; + preloadOpts.loadTitles = mAccessibilityManager.isEnabled(); plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId); mRecentsTaskLoader.loadTasks(plan, launchOpts); } diff --git a/quickstep/src/com/android/quickstep/RecentsRootView.java b/quickstep/src/com/android/quickstep/RecentsRootView.java index 3c69dbfd64..24785f9316 100644 --- a/quickstep/src/com/android/quickstep/RecentsRootView.java +++ b/quickstep/src/com/android/quickstep/RecentsRootView.java @@ -21,17 +21,19 @@ import android.graphics.Rect; import android.util.AttributeSet; import com.android.launcher3.BaseActivity; -import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; import com.android.launcher3.util.Themes; +import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; -public class RecentsRootView extends InsettableFrameLayout { +public class RecentsRootView extends BaseDragLayer { private final BaseActivity mActivity; public RecentsRootView(Context context, AttributeSet attrs) { super(context, attrs); mActivity = BaseActivity.fromContext(context); + mControllers = new TouchController[0]; } @TargetApi(23) diff --git a/quickstep/src/com/android/quickstep/SnapshotDragView.java b/quickstep/src/com/android/quickstep/SnapshotDragView.java deleted file mode 100644 index 2ef39422d2..0000000000 --- a/quickstep/src/com/android/quickstep/SnapshotDragView.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.quickstep; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.view.MotionEvent; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.Insettable; -import com.android.launcher3.Launcher; -import com.android.systemui.shared.recents.view.AnimateableViewBounds; - -/** - * Floating view which shows the task snapshot allowing it to be dragged and placed. - */ -public class SnapshotDragView extends AbstractFloatingView implements Insettable { - - private final Launcher mLauncher; - private final Bitmap mSnapshot; - private final AnimateableViewBounds mViewBounds; - - public SnapshotDragView(Launcher launcher, Bitmap snapshot) { - super(launcher, null); - mLauncher = launcher; - mSnapshot = snapshot; - mViewBounds = new AnimateableViewBounds(this, 0); - setWillNotDraw(false); - setClipToOutline(true); - setOutlineProvider(mViewBounds); - } - - AnimateableViewBounds getViewBounds() { - return mViewBounds; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mSnapshot != null) { - setMeasuredDimension(mSnapshot.getWidth(), mSnapshot.getHeight()); - } else { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - @Override - public void setInsets(Rect insets) { - - } - - @Override - protected void onDraw(Canvas canvas) { - if (mSnapshot != null) { - canvas.drawBitmap(mSnapshot, 0, 0, null); - } - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - return false; - } - - @Override - protected void handleClose(boolean animate) { - // We dont suupport animate. - mLauncher.getDragLayer().removeView(this); - } - - @Override - public void logActionCommand(int command) { - // We should probably log the weather - } - - @Override - protected boolean isOfType(int type) { - return (type & TYPE_QUICKSTEP_PREVIEW) != 0; - } -} diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java index eebfb91654..2ebf2525f5 100644 --- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java @@ -30,13 +30,15 @@ import android.view.View; import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.InstantAppResolver; import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; @@ -70,11 +72,12 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { return null; } - public View.OnClickListener getOnClickListener(final Launcher launcher, final TaskView view) { + public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) { Task task = view.getTask(); ShortcutInfo dummyInfo = new ShortcutInfo(); @@ -82,14 +85,14 @@ public class TaskSystemShortcut extends SystemShortcut ComponentName component = task.getTopComponent(); dummyInfo.intent.setComponent(component); dummyInfo.user = UserHandle.of(task.key.userId); - dummyInfo.title = TaskUtils.getTitle(launcher, task); + dummyInfo.title = TaskUtils.getTitle(activity, task); - return getOnClickListenerForTask(launcher, task, dummyInfo); + return getOnClickListenerForTask(activity, task, dummyInfo); } - protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher, - final Task task, final ItemInfo dummyInfo) { - return mSystemShortcut.getOnClickListener(launcher, dummyInfo); + protected View.OnClickListener getOnClickListenerForTask( + BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) { + return mSystemShortcut.getOnClickListener(activity, dummyInfo); } public static class AppInfo extends TaskSystemShortcut { @@ -98,10 +101,13 @@ public class TaskSystemShortcut extends SystemShortcut } } - public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener { + public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener, + DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener { private Handler mHandler; + private RecentsView mRecentsView; private TaskView mTaskView; + private BaseDraggingActivity mActivity; public SplitScreen() { super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen); @@ -109,22 +115,27 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) { - if (launcher.getDeviceProfile().isMultiWindowMode) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, TaskView taskView) { + if (activity.getDeviceProfile().isMultiWindowMode) { return null; } final Task task = taskView.getTask(); + final int taskId = task.key.id; if (!task.isDockable) { return null; } + mActivity = activity; + mRecentsView = activity.getOverviewPanel(); mTaskView = taskView; + final TaskThumbnailView thumbnailView = taskView.getThumbnail(); return (v -> { - AbstractFloatingView.closeOpenViews(launcher, true, + AbstractFloatingView.closeOpenViews(activity, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); - if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id, + if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId, ActivityOptionsCompat.makeSplitScreenOptions(true))) { - ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy(); + ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy(); try { sysUiProxy.onSplitScreenInvoked(); } catch (RemoteException e) { @@ -132,26 +143,35 @@ public class TaskSystemShortcut extends SystemShortcut return; } + // Add a device profile change listener to kick off animating the side tasks + // once we enter multiwindow mode and relayout + activity.addOnDeviceProfileChangeListener(this); + final Runnable animStartedListener = () -> { + // Hide the task view and wait for the window to be resized + // TODO: Consider animating in launcher and do an in-place start activity + // afterwards + mRecentsView.addIgnoreResetTask(mTaskView); + mTaskView.setAlpha(0f); mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this); - launcher.getOverviewPanel().removeView(taskView); }; final int[] position = new int[2]; - taskView.getLocationOnScreen(position); - final int width = (int) (taskView.getWidth() * taskView.getScaleX()); - final int height = (int) (taskView.getHeight() * taskView.getScaleY()); + thumbnailView.getLocationOnScreen(position); + final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX()); + final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY()); final Rect taskBounds = new Rect(position[0], position[1], position[0] + width, position[1] + height); Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap( - taskBounds.width(), taskBounds.height(), taskView, 1f, Color.BLACK); + taskBounds.width(), taskBounds.height(), thumbnailView, 1f, + Color.BLACK); AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) { @Override public List composeSpecs() { return Collections.singletonList(new AppTransitionAnimationSpecCompat( - task.key.id, thumbnail, taskBounds)); + taskId, thumbnail, taskBounds)); } }; WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( @@ -166,6 +186,24 @@ public class TaskSystemShortcut extends SystemShortcut WindowManagerWrapper.getInstance().endProlongedAnimations(); return true; } + + @Override + public void onDeviceProfileChanged(DeviceProfile dp) { + mActivity.removeOnDeviceProfileChangeListener(this); + if (dp.isMultiWindowMode) { + mTaskView.getRootView().addOnLayoutChangeListener(this); + } + } + + @Override + public void onLayoutChange(View v, int l, int t, int r, int b, + int oldL, int oldT, int oldR, int oldB) { + mTaskView.getRootView().removeOnLayoutChangeListener(this); + mRecentsView.removeIgnoreResetTask(mTaskView); + + // Start animating in the side pages once launcher has been resized + mRecentsView.dismissTask(mTaskView, false, false); + } } public static class Pin extends TaskSystemShortcut { @@ -178,8 +216,9 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) { - ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy(); + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, TaskView taskView) { + ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy(); if (sysUiProxy == null) { return null; } @@ -211,11 +250,11 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task, - ItemInfo itemInfo) { - if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher, + protected View.OnClickListener getOnClickListenerForTask( + BaseDraggingActivity activity, Task task, ItemInfo itemInfo) { + if (InstantAppResolver.newInstance(activity).isInstantApp(activity, task.getTopComponent().getPackageName())) { - return mSystemShortcut.createOnClickListener(launcher, itemInfo); + return mSystemShortcut.createOnClickListener(activity, itemInfo); } return null; } diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java index b31d42fb6c..2df951b86e 100644 --- a/quickstep/src/com/android/quickstep/TaskUtils.java +++ b/quickstep/src/com/android/quickstep/TaskUtils.java @@ -16,12 +16,12 @@ package com.android.quickstep; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.UserHandle; import android.util.Log; -import com.android.launcher3.Launcher; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.systemui.shared.recents.model.Task; @@ -34,10 +34,10 @@ public class TaskUtils { private static final String TAG = "TaskUtils"; - public static CharSequence getTitle(Launcher launcher, Task task) { - LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(launcher); - UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(launcher); - PackageManager packageManager = launcher.getPackageManager(); + public static CharSequence getTitle(Context context, Task task) { + LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context); + UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context); + PackageManager packageManager = context.getPackageManager(); UserHandle user = UserHandle.of(task.key.userId); ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo( task.getTopComponent().getPackageName(), 0, user); diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java index 768fbda738..4e351595ff 100644 --- a/quickstep/src/com/android/quickstep/TouchConsumer.java +++ b/quickstep/src/com/android/quickstep/TouchConsumer.java @@ -29,21 +29,14 @@ import java.util.function.Consumer; @FunctionalInterface public interface TouchConsumer extends Consumer { - static boolean isInteractionQuick(@InteractionType int interactionType) { - return interactionType == INTERACTION_QUICK_SCRUB || - interactionType == INTERACTION_QUICK_SWITCH; - } - @IntDef(flag = true, value = { INTERACTION_NORMAL, - INTERACTION_QUICK_SWITCH, INTERACTION_QUICK_SCRUB }) @Retention(RetentionPolicy.SOURCE) @interface InteractionType {} int INTERACTION_NORMAL = 0; - int INTERACTION_QUICK_SWITCH = 1; - int INTERACTION_QUICK_SCRUB = 2; + int INTERACTION_QUICK_SCRUB = 1; default void reset() { } @@ -72,4 +65,6 @@ public interface TouchConsumer extends Consumer { default boolean forceToLauncherConsumer() { return false; } + + default void onShowOverviewFromAltTab() {} } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index a522ef184e..cc49dc743e 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -23,6 +23,7 @@ import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.LauncherState.FAST_OVERVIEW; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; @@ -47,7 +48,6 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; -import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.IOverviewProxy; @@ -61,7 +61,7 @@ import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; @TargetApi(Build.VERSION_CODES.O) public class TouchInteractionService extends Service { - public static final boolean DEBUG_SHOW_OVERVIEW_BUTTON = false; + public static final boolean DEBUG_OPEN_OVERVIEW_VIA_ALT_TAB = false; private static final SparseArray sMotionEventNames; @@ -86,7 +86,7 @@ public class TouchInteractionService extends Service { @Override public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException { TraceHelper.beginSection("SysUiBinder"); - onBinderPreMotionEvent(downHitTarget); + setupTouchConsumer(downHitTarget); TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget); } @@ -106,15 +106,7 @@ public class TouchInteractionService extends Service { mRecentsModel.setSystemUiProxy(mISystemUiProxy); RemoteRunnable.executeSafely(() -> mISystemUiProxy.setRecentsOnboardingText( getResources().getString(R.string.recents_swipe_up_onboarding))); - Launcher launcher = (Launcher) LauncherAppState.getInstance( - TouchInteractionService.this).getModel().getCallback(); - UiFactory.onLauncherStateOrFocusChanged(launcher); - } - - @Override - public void onQuickSwitch() { - mEventQueue.onQuickSwitch(); - TraceHelper.endSection("SysUiBinder", "onQuickSwitch"); + mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy); } @Override @@ -140,10 +132,29 @@ public class TouchInteractionService extends Service { } @Override - public void onOverviewShown(boolean triggeredFromAltTab) { } + public void onOverviewShown(boolean triggeredFromAltTab) { + if (DEBUG_OPEN_OVERVIEW_VIA_ALT_TAB) { + if (triggeredFromAltTab) { + setupTouchConsumer(HIT_TARGET_NONE); + mEventQueue.onOverviewShownFromAltTab(); + } + } + } @Override - public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { } + public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { + if (DEBUG_OPEN_OVERVIEW_VIA_ALT_TAB) { + if (triggeredFromAltTab && !triggeredFromHomeKey) { + // onOverviewShownFromAltTab initiates quick scrub. Ending it here. + mEventQueue.onQuickScrubEnd(); + } + } + } + + @Override + public void onQuickStep(MotionEvent motionEvent) { + + } }; private final TouchConsumer mNoOpTouchConsumer = (ev) -> {}; @@ -160,6 +171,7 @@ public class TouchInteractionService extends Service { private MainThreadExecutor mMainThreadExecutor; private ISystemUiProxy mISystemUiProxy; private OverviewCommandHelper mOverviewCommandHelper; + private OverviewInteractionState mOverviewInteractionState; private Choreographer mMainThreadChoreographer; private Choreographer mBackgroundThreadChoreographer; @@ -173,6 +185,7 @@ public class TouchInteractionService extends Service { mOverviewCommandHelper = new OverviewCommandHelper(this); mMainThreadChoreographer = Choreographer.getInstance(); mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer); + mOverviewInteractionState = OverviewInteractionState.getInstance(this); sConnected = true; @@ -193,7 +206,7 @@ public class TouchInteractionService extends Service { return mMyBinder; } - private void onBinderPreMotionEvent(@HitTarget int downHitTarget) { + private void setupTouchConsumer(@HitTarget int downHitTarget) { mEventQueue.reset(); TouchConsumer oldConsumer = mEventQueue.getConsumer(); if (oldConsumer.deferNextEventToMainThread()) { @@ -221,7 +234,8 @@ public class TouchInteractionService extends Service { tracker = VelocityTracker.obtain(); } return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel, - mOverviewCommandHelper.homeIntent, mISystemUiProxy, mMainThreadExecutor, + mOverviewCommandHelper.homeIntent, + mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor, mBackgroundThreadChoreographer, downHitTarget, tracker); } } @@ -314,14 +328,10 @@ public class TouchInteractionService extends Service { if (mInvalidated) { return; } - if (TouchConsumer.isInteractionQuick(interactionType)) { + if (interactionType == INTERACTION_QUICK_SCRUB) { Runnable action = () -> { - Runnable onComplete = null; - if (interactionType == INTERACTION_QUICK_SWITCH) { - onComplete = mQuickScrubController::onQuickSwitch; - } LauncherState fromState = mLauncher.getStateManager().getState(); - mLauncher.getStateManager().goToState(FAST_OVERVIEW, true, onComplete); + mLauncher.getStateManager().goToState(FAST_OVERVIEW, true); mQuickScrubController.onQuickScrubStart(fromState == NORMAL); }; diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index af09842790..25f2f87601 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -15,22 +15,16 @@ */ package com.android.quickstep; -import static com.android.launcher3.LauncherState.FAST_OVERVIEW; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; +import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; -import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; -import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION; +import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION; import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB; -import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH; -import static com.android.quickstep.TouchConsumer.isInteractionQuick; -import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously; +import static com.android.systemui.shared.recents.utilities.Utilities + .postAtFrontOfQueueAsynchronously; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.animation.Animator; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; @@ -41,7 +35,6 @@ import android.graphics.Matrix.ScaleToFit; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -import android.metrics.LogMaker; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -51,16 +44,15 @@ import android.support.annotation.WorkerThread; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver.OnDrawListener; +import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; @@ -69,8 +61,10 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; +import com.android.quickstep.ActivityControlHelper.LayoutListener; import com.android.quickstep.TouchConsumer.InteractionType; +import com.android.quickstep.util.SysuiEventLogger; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -84,42 +78,8 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.util.StringJoiner; -class EventLogTags { - private EventLogTags() { - } // don't instantiate - - /** 524292 sysui_multi_action (content|4) */ - public static final int SYSUI_MULTI_ACTION = 524292; - - public static void writeSysuiMultiAction(Object[] content) { - android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content); - } -} - -class MetricsLogger { - private static MetricsLogger sMetricsLogger; - - private static MetricsLogger getLogger() { - if (sMetricsLogger == null) { - sMetricsLogger = new MetricsLogger(); - } - return sMetricsLogger; - } - - protected void saveLog(Object[] rep) { - EventLogTags.writeSysuiMultiAction(rep); - } - - public void write(LogMaker content) { - if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { - content.setType(4/*MetricsEvent.TYPE_ACTION*/); - } - saveLog(content.serialize()); - } -} - @TargetApi(Build.VERSION_CODES.O) -public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { +public class WindowTransformSwipeHandler { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); private static final boolean DEBUG_STATES = false; @@ -142,9 +102,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // States for quick switch/scrub private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10; - private static final int STATE_QUICK_SWITCH = 1 << 11; - private static final int STATE_QUICK_SCRUB_START = 1 << 12; - private static final int STATE_QUICK_SCRUB_END = 1 << 13; + private static final int STATE_QUICK_SCRUB_START = 1 << 11; + private static final int STATE_QUICK_SCRUB_END = 1 << 12; private static final int LAUNCHER_UI_STATES = STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE @@ -193,6 +152,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // The clip rect in source app window coordinates private final Rect mClipRect = new Rect(); private final RectFEvaluator mRectFEvaluator = new RectFEvaluator(); + protected Runnable mGestureEndCallback; + protected boolean mIsGoingToHome; private DeviceProfile mDp; private int mTransitionDragLength; @@ -206,12 +167,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { private final Context mContext; private final int mRunningTaskId; + private final ActivityControlHelper mActivityControlHelper; + private final ActivityInitListener mActivityInitListener; private MultiStateCallback mStateCallback; private AnimatorPlaybackController mLauncherTransitionController; - private Launcher mLauncher; - private LauncherLayoutListener mLauncherLayoutListener; + private T mActivity; + private LayoutListener mLayoutListener; private RecentsView mRecentsView; private QuickScrubController mQuickScrubController; @@ -232,12 +195,16 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { private Matrix mTmpMatrix = new Matrix(); private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs) { + WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, + ActivityControlHelper controller) { mContext = context; mRunningTaskId = runningTaskInfo.id; mTouchTimeMs = touchTimeMs; + mActivityControlHelper = controller; + mActivityInitListener = mActivityControlHelper + .createActivityInitListener(this::onActivityInit); + // Register the input consumer on the UI thread, to ensure that it runs after any pending // unregister calls mMainExecutor.execute(mInputConsumer::registerInputConsumer); @@ -283,13 +250,10 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, this::invalidateHandlerWithLauncher); - mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SWITCH, - this::onQuickInteractionStart); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SCRUB_START, - this::onQuickInteractionStart); - - mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SWITCH_TO_SCREENSHOT_COMPLETE - | STATE_QUICK_SWITCH, this::switchToFinalAppAfterQuickSwitch); + this::onQuickScrubStart); + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SCRUB_START + | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SWITCH_TO_SCREENSHOT_COMPLETE | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub); } @@ -310,7 +274,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mSourceStackBounds.height() - mSourceInsets.bottom); Rect tempRect = new Rect(); - RecentsView.getPageRect(dp, mContext, tempRect); + mTransitionDragLength = mActivityControlHelper + .getSwipeUpDestinationAndLength(dp, mContext, tempRect); mTargetRect.set(tempRect); mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left, @@ -331,14 +296,15 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0), Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0)); mSourceRect.set(scaledTargetRect); + } - Rect targetInsets = dp.getInsets(); - if (dp.isVerticalBarLayout()) { - int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; - mTransitionDragLength = dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; - } else { - mTransitionDragLength = dp.heightPx - tempRect.bottom; - } + public int getTransitionLength() { + return mTransitionDragLength; + } + + public RectF getTargetRect(Point outWindowSize) { + outWindowSize.set(mDp.widthPx, mDp.heightPx); + return mInitialTargetRect; } private long getFadeInDuration() { @@ -353,40 +319,39 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } } - @Override - protected boolean init(final Launcher launcher, boolean alreadyOnHome) { - if (launcher == mLauncher) { + public void initWhenReady() { + mActivityInitListener.register(); + } + + private boolean onActivityInit(final T activity, Boolean alreadyOnHome) { + if (mActivity == activity) { return true; } - if (mLauncher != null) { + if (mActivity != null) { // The launcher may have been recreated as a result of device rotation. int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES; initStateCallbacks(); mStateCallback.setState(oldState); - mLauncherLayoutListener.setHandler(null); + mLayoutListener.setHandler(null); } mWasLauncherAlreadyVisible = alreadyOnHome; - mLauncher = launcher; + mActivity = activity; - // For the duration of the gesture, lock the screen orientation to ensure that we do not - // rotate mid-quickscrub - mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK); - - mRecentsView = mLauncher.getOverviewPanel(); + mRecentsView = activity.getOverviewPanel(); mQuickScrubController = mRecentsView.getQuickScrubController(); - mLauncherLayoutListener = new LauncherLayoutListener(mLauncher); + mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity); mStateCallback.setState(STATE_LAUNCHER_PRESENT); if (alreadyOnHome) { - onLauncherStart(launcher); + onLauncherStart(activity); } else { - launcher.setOnStartCallback(this::onLauncherStart); + activity.setOnStartCallback(this::onLauncherStart); } return true; } - private void onLauncherStart(final Launcher launcher) { - if (mLauncher != launcher) { + private void onLauncherStart(final T activity) { + if (mActivity != activity) { return; } if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) { @@ -394,31 +359,20 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } mStateCallback.setState(STATE_LAUNCHER_STARTED); - LauncherState startState = mLauncher.getStateManager().getState(); - if (startState.disableRestore) { - startState = mLauncher.getStateManager().getRestState(); - } - mLauncher.getStateManager().setRestState(startState); + mActivityControlHelper.prepareRecentsUI(mActivity, mWasLauncherAlreadyVisible); + AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible); - AbstractFloatingView.closeAllOpenViews(mLauncher, mWasLauncherAlreadyVisible); - - - if (mWasLauncherAlreadyVisible && !mLauncher.getAppTransitionManager().isAnimating()) { - DeviceProfile dp = mLauncher.getDeviceProfile(); - long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); - mLauncherTransitionController = mLauncher.getStateManager() - .createAnimationToNewWorkspace(OVERVIEW, accuracy); + if (mWasLauncherAlreadyVisible) { + mLauncherTransitionController = mActivityControlHelper + .createControllerForVisibleActivity(activity); mLauncherTransitionController.dispatchOnStart(); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN); } else { TraceHelper.beginSection("WTS-init"); - mLauncher.getStateManager().goToState(OVERVIEW, false); - TraceHelper.partitionSection("WTS-init", "State changed"); - // TODO: Implement a better animation for fading in - View rootView = mLauncher.getRootView(); + View rootView = activity.getRootView(); rootView.setAlpha(0); rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { @@ -427,21 +381,18 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { TraceHelper.endSection("WTS-init", "Launcher frame is drawn"); rootView.post(() -> rootView.getViewTreeObserver().removeOnDrawListener(this)); - if (launcher != mLauncher) { + if (activity != mActivity) { return; } mStateCallback.setState(STATE_LAUNCHER_DRAWN); } }); - - // Optimization, hide the all apps view to prevent layout while initializing - mLauncher.getAppsView().setVisibility(View.GONE); } mRecentsView.showTask(mRunningTaskId); mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */); - mLauncherLayoutListener.open(); + mLayoutListener.open(); } public void setLauncherOnDrawCallback(Runnable callback) { @@ -449,7 +400,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } private void launcherFrameDrawn() { - View rootView = mLauncher.getRootView(); + View rootView = mActivity.getRootView(); if (rootView.getAlpha() < 1) { if (mGestureStarted) { final MultiStateCallback callback = mStateCallback; @@ -468,19 +419,12 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } private void initializeLauncherAnimationController() { - mLauncherLayoutListener.setHandler(this); + mLayoutListener.setHandler(this); onLauncherLayoutChanged(); final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs; - // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for - // "Recents" activity for app transition tests for the app-to-recents case. - final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); - builder.setPackageName("com.android.systemui"); - builder.addTaggedData(871/*FIELD_CLASS_NAME*/, - "com.android.systemui.recents.RecentsActivity"); - builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, - transitionDelay); - mMetricsLogger.write(builder); + SysuiEventLogger.writeDummyRecentsTransition(transitionDelay); + if (LatencyTrackerCompat.isEnabled(mContext)) { LatencyTrackerCompat.logToggleRecents((int) transitionDelay); } @@ -491,23 +435,16 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { throw new IllegalArgumentException( "Can't change interaction type from " + mInteractionType); } - if (!isInteractionQuick(interactionType)) { + if (interactionType != INTERACTION_QUICK_SCRUB) { throw new IllegalArgumentException( "Can't change interaction type to " + interactionType); } mInteractionType = interactionType; - setStateOnUiThread(interactionType == INTERACTION_QUICK_SWITCH - ? STATE_QUICK_SWITCH : STATE_QUICK_SCRUB_START); + setStateOnUiThread(STATE_QUICK_SCRUB_START); // Start the window animation without waiting for launcher. - animateToProgress(1f, QUICK_SWITCH_START_DURATION); - } - - private void onQuickInteractionStart() { - mLauncher.getStateManager().goToState(FAST_OVERVIEW, - mWasLauncherAlreadyVisible || mGestureStarted); - mQuickScrubController.onQuickScrubStart(false); + animateToProgress(1f, QUICK_SCRUB_START_DURATION); } @WorkerThread @@ -520,32 +457,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } /** - * Called by {@link #mLauncherLayoutListener} when launcher layout changes + * Called by {@link #mLayoutListener} when launcher layout changes */ public void onLauncherLayoutChanged() { - initTransitionEndpoints(mLauncher.getDeviceProfile()); + initTransitionEndpoints(mActivity.getDeviceProfile()); if (!mWasLauncherAlreadyVisible) { - float startProgress; - AllAppsTransitionController controller = mLauncher.getAllAppsController(); - - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - startProgress = 1; - } else { - float scrollRange = Math.max(controller.getShiftRange(), 1); - startProgress = (mTransitionDragLength / scrollRange) + 1; - } - AnimatorSet anim = new AnimatorSet(); - ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, - startProgress, OVERVIEW.getVerticalProgress(mLauncher)); - shiftAnim.setInterpolator(LINEAR); - anim.play(shiftAnim); - - // TODO: Link this animation to state animation, so that it is cancelled - // automatically on state change - anim.setDuration(mTransitionDragLength * 2); - mLauncherTransitionController = - AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2); + mLauncherTransitionController = mActivityControlHelper + .createControllerForHiddenActivity(mActivity, mTransitionDragLength); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); } } @@ -558,7 +477,12 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { if (mRecentsAnimationWrapper.controller != null) { RectF currentRect; synchronized (mTargetRect) { - currentRect = mRectFEvaluator.evaluate(shift, mSourceRect, mTargetRect); + Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB + ? ACCEL_2 : LINEAR; + float interpolated = interpolator.getInterpolation(shift); + currentRect = mRectFEvaluator.evaluate(interpolated, mSourceRect, mTargetRect); + // Stay lined up with the center of the target, since it moves for quick scrub. + currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0); } mClipRect.left = (int) (mSourceWindowClipInsets.left * shift); @@ -598,13 +522,18 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { View firstTask = mRecentsView.getPageAt(0); int scrollForFirstTask = mRecentsView.getScrollForPage(0); int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX()); - if (offsetFromFirstTask != 0) { - synchronized (mTargetRect) { - mTargetRect.set(mInitialTargetRect); - Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX()); - float offsetX = offsetFromFirstTask + firstTask.getTranslationX(); - mTargetRect.offset(offsetX, 0); - } + synchronized (mTargetRect) { + mTargetRect.set(mInitialTargetRect); + Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX()); + float offsetX = offsetFromFirstTask + firstTask.getTranslationX(); + float offsetY = mRecentsView.getTranslationY(); + mTargetRect.offset(offsetX, offsetY); + } + if (mRecentsAnimationWrapper.controller != null) { + + // TODO: This logic is spartanic! + mRecentsAnimationWrapper.controller.setAnimationTargetsBehindSystemBars( + shift < 0.12f); } }; if (Looper.getMainLooper() == Looper.myLooper()) { @@ -652,14 +581,13 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } } } - mRecentsAnimationWrapper.setController(controller, apps); setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); } public void onRecentsAnimationCanceled() { mRecentsAnimationWrapper.setController(null, null); - clearReference(); + mActivityInitListener.unregister(); setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); } @@ -675,9 +603,10 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { * on both background and UI threads */ private void notifyGestureStarted() { - final Launcher curLauncher = mLauncher; - if (curLauncher != null) { - curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible); + final T curActivity = mActivity; + if (curActivity != null) { + mActivityControlHelper.onQuickstepGestureStarted( + curActivity, mWasLauncherAlreadyVisible); } } @@ -761,25 +690,20 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mGestureEndCallback.run(); } - clearReference(); + mActivityInitListener.unregister(); mInputConsumer.unregisterInputConsumer(); } private void invalidateHandlerWithLauncher() { mLauncherTransitionController = null; - mLauncherLayoutListener.setHandler(null); - mLauncherLayoutListener.close(false); - - // Restore the requested orientation to the user preference after the gesture has ended - mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE); + mLayoutListener.finish(); mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */); } private void resetStateForAnimationCancel() { - LauncherState startState = mLauncher.getStateManager().getRestState(); - boolean animate = mWasLauncherAlreadyVisible || mGestureStarted; - mLauncher.getStateManager().goToState(startState, animate); + boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted; + mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible); } public void layoutListenerClosed() { @@ -808,17 +732,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { if (taskView != null) { // Defer finishing the animation until the next launcher frame with the // new thumbnail - ViewOnDrawExecutor executor = new ViewOnDrawExecutor() { - @Override - public void onViewDetachedFromWindow(View v) { - if (!isCompleted()) { - runAllTasks(); - } - } - }; - executor.attachTo(mLauncher, taskView, - false /* waitForLoadAnimation */); - executor.execute(finishTransitionRunnable); + mActivityControlHelper.executeOnNextDraw(mActivity, taskView, + finishTransitionRunnable); finishTransitionPosted = true; } } @@ -834,8 +749,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } private void setupLauncherUiAfterSwipeUpAnimation() { - // Re apply state in case we did something funky during the transition. - mLauncher.getStateManager().reapplyState(); + mActivityControlHelper.onSwipeUpComplete(mActivity); // Animate the first icon. mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */); @@ -843,21 +757,13 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { reset(); } - public void onQuickScrubEnd() { - setStateOnUiThread(STATE_QUICK_SCRUB_END); + private void onQuickScrubStart() { + mActivityControlHelper.onQuickInteractionStart(mActivity, mWasLauncherAlreadyVisible); + mQuickScrubController.onQuickScrubStart(false); } - private void switchToFinalAppAfterQuickSwitch() { - mQuickScrubController.onQuickSwitch(); - } - - private void switchToFinalAppAfterQuickScrub() { - mQuickScrubController.onQuickScrubEnd(); - - // Normally this is handled in reset(), but since we are still scrubbing after the - // transition into recents, we need to defer the handler invalidation for quick scrub until - // after the gesture ends - setStateOnUiThread(STATE_HANDLER_INVALIDATED); + private void onFinishedTransitionToQuickScrub() { + mQuickScrubController.onFinishedTransitionToQuickScrub(); } public void onQuickScrubProgress(float progress) { @@ -870,6 +776,19 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mQuickScrubController.onQuickScrubProgress(progress); } + public void onQuickScrubEnd() { + setStateOnUiThread(STATE_QUICK_SCRUB_END); + } + + private void switchToFinalAppAfterQuickScrub() { + mQuickScrubController.onQuickScrubEnd(); + + // Normally this is handled in reset(), but since we are still scrubbing after the + // transition into recents, we need to defer the handler invalidation for quick scrub until + // after the gesture ends + setStateOnUiThread(STATE_HANDLER_INVALIDATED); + } + private void debugNewState(int stateFlag) { if (!DEBUG_STATES) { return; @@ -889,4 +808,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to " + currentStateStr); } + + public void setGestureEndCallback(Runnable gestureEndCallback) { + mGestureEndCallback = gestureEndCallback; + } } diff --git a/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java new file mode 100644 index 0000000000..d474ded905 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import android.metrics.LogMaker; +import android.util.EventLog; + +/** + * Utility class for writing logs on behalf of systemUI + */ +public class SysuiEventLogger { + + /** 524292 sysui_multi_action (content|4) */ + public static final int SYSUI_MULTI_ACTION = 524292; + + private static void write(LogMaker content) { + if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { + content.setType(4/*MetricsEvent.TYPE_ACTION*/); + } + EventLog.writeEvent(SYSUI_MULTI_ACTION, content.serialize()); + } + + public static void writeDummyRecentsTransition(long transitionDelay) { + // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for + // "Recents" activity for app transition tests for the app-to-recents case. + final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); + builder.setPackageName("com.android.systemui"); + builder.addTaggedData(871/*FIELD_CLASS_NAME*/, + "com.android.systemui.recents.RecentsActivity"); + builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, + transitionDelay); + write(builder); + } +} diff --git a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java similarity index 83% rename from quickstep/src/com/android/quickstep/LauncherLayoutListener.java rename to quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java index fbdbe7a141..6b7143d048 100644 --- a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java +++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.views; + +import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import android.graphics.Rect; import android.view.MotionEvent; @@ -21,11 +23,14 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; +import com.android.quickstep.ActivityControlHelper.LayoutListener; +import com.android.quickstep.WindowTransformSwipeHandler; /** * Floating view which shows the task snapshot allowing it to be dragged and placed. */ -public class LauncherLayoutListener extends AbstractFloatingView implements Insettable { +public class LauncherLayoutListener extends AbstractFloatingView + implements Insettable, LayoutListener { private final Launcher mLauncher; private WindowTransformSwipeHandler mHandler; @@ -36,6 +41,7 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse setVisibility(INVISIBLE); } + @Override public void setHandler(WindowTransformSwipeHandler handler) { mHandler = handler; } @@ -65,6 +71,7 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse } } + @Override public void open() { if (!mIsOpen) { mLauncher.getDragLayer().addView(this); @@ -86,4 +93,11 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse protected boolean isOfType(int type) { return (type & TYPE_QUICKSTEP_PREVIEW) != 0; } + + @Override + public void finish() { + setHandler(null); + close(false); + mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE); + } } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 7989e845ca..861b5fa038 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -17,20 +17,12 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.NORMAL; +import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.Shader; -import android.graphics.Shader.TileMode; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.AttributeSet; +import android.util.FloatProperty; import android.widget.FrameLayout; import com.android.launcher3.DeviceProfile; @@ -41,13 +33,40 @@ import com.android.launcher3.R; /** * {@link RecentsView} used in Launcher activity */ +@TargetApi(Build.VERSION_CODES.O) public class LauncherRecentsView extends RecentsView implements Insettable { - private Bitmap mScrim; - private Paint mFadePaint; - private Shader mFadeShader; - private Matrix mFadeMatrix; - private boolean mScrimOnLeft; + public static final FloatProperty TRANSLATION_X_FACTOR = + new FloatProperty("translationXFactor") { + + @Override + public void setValue(LauncherRecentsView view, float v) { + view.setTranslationXFactor(v); + } + + @Override + public Float get(LauncherRecentsView view) { + return view.mTranslationXFactor; + } + }; + + public static final FloatProperty TRANSLATION_Y_FACTOR = + new FloatProperty("translationYFactor") { + + @Override + public void setValue(LauncherRecentsView view, float v) { + view.setTranslationYFactor(v); + } + + @Override + public Float get(LauncherRecentsView view) { + return view.mTranslationYFactor; + } + }; + + private float mTranslationXFactor; + private float mTranslationYFactor; + private Rect mPagePadding = new Rect(); public LauncherRecentsView(Context context) { this(context, null); @@ -59,6 +78,7 @@ public class LauncherRecentsView extends RecentsView implements Insett public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + setContentAlpha(0); } @Override @@ -71,64 +91,39 @@ public class LauncherRecentsView extends RecentsView implements Insett setLayoutParams(lp); setPadding(padding.left, padding.top, padding.right, 0); - - if (dp.isVerticalBarLayout()) { - boolean wasScrimOnLeft = mScrimOnLeft; - mScrimOnLeft = dp.isSeascape(); - - if (mScrim == null || wasScrimOnLeft != mScrimOnLeft) { - Drawable scrim = getContext().getDrawable(mScrimOnLeft - ? R.drawable.recents_horizontal_scrim_left - : R.drawable.recents_horizontal_scrim_right); - if (scrim instanceof BitmapDrawable) { - mScrim = ((BitmapDrawable) scrim).getBitmap(); - mFadePaint = new Paint(); - mFadePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); - mFadeShader = new BitmapShader(mScrim, TileMode.CLAMP, TileMode.REPEAT); - mFadeMatrix = new Matrix(); - } else { - mScrim = null; - } - } - } else { - mScrim = null; - mFadePaint = null; - mFadeShader = null; - mFadeMatrix = null; - } - } - - @Override - public void draw(Canvas canvas) { - if (mScrim == null) { - super.draw(canvas); - return; - } - - final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; - - int length = mScrim.getWidth(); - int height = getHeight(); - int saveCount = canvas.getSaveCount(); - - int scrimLeft; - if (mScrimOnLeft) { - scrimLeft = getScrollX(); - } else { - scrimLeft = getScrollX() + getWidth() - length; - } - canvas.saveLayer(scrimLeft, 0, scrimLeft + length, height, null, flags); - super.draw(canvas); - - mFadeMatrix.setTranslate(scrimLeft, 0); - mFadeShader.setLocalMatrix(mFadeMatrix); - mFadePaint.setShader(mFadeShader); - canvas.drawRect(scrimLeft, 0, scrimLeft + length, height, mFadePaint); - canvas.restoreToCount(saveCount); + mPagePadding.set(padding); + mPagePadding.top += getResources().getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); } @Override protected void onAllTasksRemoved() { mActivity.getStateManager().goToState(NORMAL); } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + int width = right - left; + setTranslationX(mTranslationXFactor * (mIsRtl ? -width : width)); + setTranslationYFactor(mTranslationYFactor); + } + + public void setTranslationXFactor(float translationFactor) { + mTranslationXFactor = translationFactor; + setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth())); + } + + public float getTranslationXFactor() { + return mTranslationXFactor; + } + + public void setTranslationYFactor(float translationFactor) { + mTranslationYFactor = translationFactor; + setTranslationY(mTranslationYFactor * (mPagePadding.bottom - mPagePadding.top)); + } + + public float getTranslationYFactor() { + return mTranslationYFactor; + } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 23e6e5bb20..8901e6d8c6 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -18,6 +18,7 @@ package com.android.quickstep.views; 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 android.animation.AnimatorSet; @@ -30,8 +31,11 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.graphics.Rect; import android.os.Build; +import android.util.ArraySet; import android.util.AttributeSet; +import android.util.FloatProperty; import android.util.SparseBooleanArray; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -40,6 +44,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.PendingAnimation; import com.android.quickstep.QuickScrubController; @@ -62,7 +67,23 @@ import java.util.ArrayList; public abstract class RecentsView extends PagedView implements OnSharedPreferenceChangeListener { + public static final FloatProperty CONTENT_ALPHA = + new FloatProperty("contentAlpha") { + + + @Override + public void setValue(RecentsView recentsView, float v) { + recentsView.setContentAlpha(v); + } + + @Override + public Float get(RecentsView recentsView) { + return recentsView.mContentAlpha; + } + }; + private static final String PREF_FLIP_RECENTS = "pref_flip_recents"; + private static final int DISMISS_TASK_DURATION = 300; private static final Rect sTempStableInsets = new Rect(); @@ -104,6 +125,11 @@ public abstract class RecentsView private PendingAnimation mPendingAnimation; + private float mContentAlpha = 1; + + // Keeps track of task views whose visual state should not be reset + private ArraySet mIgnoreResetTaskViews = new ArraySet<>(); + public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing)); @@ -261,7 +287,10 @@ public abstract class RecentsView public void resetTaskVisuals() { for (int i = getChildCount() - 1; i >= 0; i--) { - ((TaskView) getChildAt(i)).resetVisualProperties(); + TaskView taskView = (TaskView) getChildAt(i); + if (!mIgnoreResetTaskViews.contains(taskView)) { + taskView.resetVisualProperties(); + } } updateCurveProperties(); @@ -294,12 +323,10 @@ public abstract class RecentsView float overviewHeight, overviewWidth; if (profile.isVerticalBarLayout()) { - float scrimLength = context.getResources() - .getDimension(R.dimen.recents_page_fade_length); float maxPadding = Math.max(padding.left, padding.right); // Use the same padding on both sides for symmetry. - float availableWidth = taskWidth - 2 * Math.max(maxPadding, scrimLength); + float availableWidth = taskWidth - 2 * maxPadding; float availableHeight = profile.availableHeightPx - padding.top - padding.bottom - sTempStableInsets.top; float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight); @@ -512,7 +539,16 @@ public abstract class RecentsView public float linearInterpolation; } - public PendingAnimation createTaskDismissAnimation(TaskView taskView, long duration) { + public void addIgnoreResetTask(TaskView taskView) { + mIgnoreResetTaskViews.add(taskView); + } + + public void removeIgnoreResetTask(TaskView taskView) { + mIgnoreResetTaskViews.remove(taskView); + } + + public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView, + boolean removeTask, long duration) { if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) { throw new IllegalStateException("Another pending animation is still running"); } @@ -543,9 +579,11 @@ public abstract class RecentsView for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child == taskView) { - addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); - addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), - duration, LINEAR, anim); + if (animateTaskView) { + addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); + addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), + duration, LINEAR, anim); + } } else { int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff; if (scrollDiff != 0) { @@ -563,12 +601,16 @@ public abstract class RecentsView } // Add a tiny bit of translation Z, so that it draws on top of other views - taskView.setTranslationZ(0.1f); + if (animateTaskView) { + taskView.setTranslationZ(0.1f); + } mPendingAnimation = pendingAnimation; mPendingAnimation.addEndListener((isSuccess) -> { if (isSuccess) { - ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id); + if (removeTask) { + ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id); + } removeView(taskView); if (getChildCount() == 0) { onAllTasksRemoved(); @@ -585,4 +627,85 @@ public abstract class RecentsView anim.setDuration(duration).setInterpolator(interpolator); set.play(anim); } + + private void snapToPageRelative(int delta) { + snapToPage((getNextPage() + getPageCount() + delta) % getPageCount()); + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + if (isVisible && !isFocused()) { + // Having focus, even in touch mode, keeps us from losing [Alt+]Tab by preventing + // switching to keyboard mode. + requestFocus(); + } + } + + public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) { + PendingAnimation pendingAnim = createTaskDismissAnimation(taskView, animateTaskView, + removeTask, DISMISS_TASK_DURATION); + AnimatorPlaybackController controller = AnimatorPlaybackController.wrap( + pendingAnim.anim, DISMISS_TASK_DURATION); + controller.dispatchOnStart(); + controller.setEndAction(() -> pendingAnim.finish(true)); + controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN); + controller.start(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_TAB: + snapToPageRelative(event.isShiftPressed() ? -1 : 1); + return true; + case KeyEvent.KEYCODE_DPAD_RIGHT: + snapToPageRelative(mIsRtl ? -1 : 1); + return true; + case KeyEvent.KEYCODE_DPAD_LEFT: + snapToPageRelative(mIsRtl ? 1 : -1); + return true; + case KeyEvent.KEYCODE_DEL: + case KeyEvent.KEYCODE_FORWARD_DEL: + dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/, + true /*removeTask*/); + return true; + case KeyEvent.KEYCODE_NUMPAD_DOT: + if (event.isAltPressed()) { + // Numpad DEL pressed while holding Alt. + dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/, + true /*removeTask*/); + return true; + } + } + } + return super.dispatchKeyEvent(event); + } + + public void snapToTaskAfterNext() { + snapToPageRelative(1); + } + + public void launchNextTask() { + final TaskView nextTask = (TaskView) getChildAt(getNextPage()); + nextTask.launchTask(true); + } + + public void setContentAlpha(float alpha) { + if (mContentAlpha == alpha) { + return; + } + mContentAlpha = alpha; + for (int i = getChildCount() - 1; i >= 0; i--) { + getChildAt(i).setAlpha(alpha); + } + setVisibility(alpha > 0 ? VISIBLE : GONE); + } + + @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + child.setAlpha(mContentAlpha); + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index 94f440dcd8..30cbcdf674 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -32,14 +32,14 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.Launcher; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.TaskSystemShortcut; import com.android.quickstep.TaskUtils; @@ -60,7 +60,7 @@ public class TaskMenuView extends AbstractFloatingView { private static final long OPEN_CLOSE_DURATION = 220; - private Launcher mLauncher; + private BaseDraggingActivity mActivity; private TextView mTaskIconAndName; private AnimatorSet mOpenCloseAnimator; private TaskView mTaskView; @@ -72,7 +72,7 @@ public class TaskMenuView extends AbstractFloatingView { public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLauncher = Launcher.getLauncher(context); + mActivity = BaseDraggingActivity.fromContext(context); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { @Override @@ -92,7 +92,7 @@ public class TaskMenuView extends AbstractFloatingView { @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - DragLayer dl = mLauncher.getDragLayer(); + BaseDragLayer dl = mActivity.getDragLayer(); if (!dl.isEventOverView(this, ev)) { // TODO: log this once we have a new container type for it? close(true); @@ -122,9 +122,9 @@ public class TaskMenuView extends AbstractFloatingView { } public static boolean showForTask(TaskView taskView) { - Launcher launcher = Launcher.getLauncher(taskView.getContext()); - final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate( - R.layout.task_menu, launcher.getDragLayer(), false); + BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext()); + final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate( + R.layout.task_menu, activity.getDragLayer(), false); return taskMenuView.populateAndShowForTask(taskView); } @@ -132,7 +132,7 @@ public class TaskMenuView extends AbstractFloatingView { if (isAttachedToWindow()) { return false; } - mLauncher.getDragLayer().addView(this); + mActivity.getDragLayer().addView(this); mTaskView = taskView; addMenuOptions(mTaskView); orientAroundTaskView(mTaskView); @@ -145,11 +145,11 @@ public class TaskMenuView extends AbstractFloatingView { int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size); icon.setBounds(0, 0, iconSize, iconSize); mTaskIconAndName.setCompoundDrawables(null, icon, null, null); - mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask())); + mTaskIconAndName.setText(TaskUtils.getTitle(getContext(), taskView.getTask())); mTaskIconAndName.setOnClickListener(v -> close(true)); for (TaskSystemShortcut menuOption : MENU_OPTIONS) { - OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView); + OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView); if (onClickListener != null) { addMenuOption(menuOption, onClickListener); } @@ -157,7 +157,7 @@ public class TaskMenuView extends AbstractFloatingView { } private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) { - DeepShortcutView menuOptionView = (DeepShortcutView) mLauncher.getLayoutInflater().inflate( + DeepShortcutView menuOptionView = (DeepShortcutView) mActivity.getLayoutInflater().inflate( R.layout.system_shortcut, this, false); menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId); menuOptionView.getBubbleText().setText(menuOption.labelResId); @@ -167,8 +167,8 @@ public class TaskMenuView extends AbstractFloatingView { private void orientAroundTaskView(TaskView taskView) { measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect); - Rect insets = mLauncher.getDragLayer().getInsets(); + mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect); + Rect insets = mActivity.getDragLayer().getInsets(); int x = sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left; setX(Utilities.isRtl(getResources()) ? -x : x); setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top); @@ -211,7 +211,7 @@ public class TaskMenuView extends AbstractFloatingView { private void closeComplete() { mIsOpen = false; - mLauncher.getDragLayer().removeView(this); + mActivity.getDragLayer().removeView(this); } private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 7a575ad253..9884a4858f 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -28,7 +28,7 @@ import android.view.ViewOutlineProvider; import android.widget.FrameLayout; import android.widget.ImageView; -import com.android.launcher3.Launcher; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.quickstep.views.RecentsView.PageCallbacks; import com.android.quickstep.views.RecentsView.ScrollState; @@ -54,6 +54,11 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback */ private static final float MAX_PAGE_SCRIM_ALPHA = 0.4f; + /** + * How much to scale down pages near the edge of the screen. + */ + private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f; + private static final long SCALE_ICON_DURATION = 120; private Task mTask; @@ -91,6 +96,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback mTask = task; mSnapshotView.bind(); task.addCallback(this); + setContentDescription(task.titleDescription); } public Task getTask() { @@ -110,7 +116,8 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback if (mTask != null) { final ActivityOptions opts; if (animate) { - opts = Launcher.getLauncher(getContext()).getActivityLaunchOptions(this, false); + opts = BaseDraggingActivity.fromContext(getContext()) + .getActivityLaunchOptions(this, false); } else { opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0); } @@ -164,6 +171,16 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation); mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA); + + float scale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR; + setScaleX(scale); + setScaleY(scale); + } + + @Override + public boolean hasOverlappingRendering() { + // TODO: Clip-out the icon region from the thumbnail, since they are overlapping. + return false; } private static final class TaskOutlineProvider extends ViewOutlineProvider { diff --git a/res/drawable/ic_drag_indicator.xml b/res/drawable/ic_drag_indicator.xml new file mode 100644 index 0000000000..d50bdd3fdf --- /dev/null +++ b/res/drawable/ic_drag_indicator.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml index 5537bc60a5..d858d3e21a 100644 --- a/res/layout/all_apps_fast_scroller.xml +++ b/res/layout/all_apps_fast_scroller.xml @@ -21,8 +21,8 @@ android:id="@+id/fast_scroller_popup" style="@style/FastScrollerPopup" android:layout_alignParentEnd="true" - android:layout_below="@+id/search_container_all_apps" - android:layout_marginEnd="@dimen/fastscroll_popup_margin" /> + android:layout_marginEnd="@dimen/fastscroll_popup_margin" + android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin" /> \ No newline at end of file diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml index c4240f80db..f88c600423 100644 --- a/res/layout/all_apps_floating_header.xml +++ b/res/layout/all_apps_floating_header.xml @@ -18,10 +18,10 @@ android:id="@+id/all_apps_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/search_container_all_apps" + android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin" android:clipToPadding="false" - android:paddingTop="@dimen/all_apps_header_top_padding" - android:orientation="vertical" > + android:orientation="vertical" + android:paddingTop="@dimen/all_apps_header_top_padding" > diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml index 2accd2d21f..fea2eeae30 100644 --- a/res/layout/all_apps_tabs.xml +++ b/res/layout/all_apps_tabs.xml @@ -20,9 +20,8 @@ android:id="@+id/all_apps_tabs_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@id/search_container_all_apps" android:layout_gravity="center_horizontal|top" - android:layout_marginTop="@dimen/all_apps_header_tab_height" + android:layout_marginTop="@dimen/all_apps_tabs_top_margin" android:clipChildren="true" android:clipToPadding="false" android:descendantFocusability="afterDescendants" diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml index 73c0e523e2..01cd92a63d 100644 --- a/res/layout/hotseat.xml +++ b/res/layout/hotseat.xml @@ -17,6 +17,16 @@ android:theme="@style/HomeScreenElementTheme" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res-auto"> + + + - - - + + + diff --git a/res/layout/launcher_preference.xml b/res/layout/launcher_preference.xml new file mode 100644 index 0000000000..ed0ea7c0e2 --- /dev/null +++ b/res/layout/launcher_preference.xml @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index 91baf7a769..eec57a5df3 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -31,7 +31,6 @@ android:layout_height="@dimen/widget_section_height" android:background="?android:attr/colorPrimary" android:drawablePadding="@dimen/widget_section_horizontal_padding" - android:ellipsize="end" android:focusable="true" android:gravity="start|center_vertical" android:paddingBottom="@dimen/widget_section_vertical_padding" diff --git a/res/layout/work_tab_bottom_user_education_view.xml b/res/layout/work_tab_bottom_user_education_view.xml index dc6854ed12..ba6a939429 100644 --- a/res/layout/work_tab_bottom_user_education_view.xml +++ b/res/layout/work_tab_bottom_user_education_view.xml @@ -20,6 +20,7 @@ android:layout_gravity="bottom" android:background="?android:attr/colorAccent" android:elevation="2dp" + android:focusable="true" android:orientation="horizontal"> "Kon geen programme kry wat by \"%1$s\" pas nie" "Soek meer programme" "Kennisgewings" + + + + "Niks meer spasie op die tuisskerm nie." "Geen plek meer in die Gunstelinge-laai nie" "Programmelys" + "Lys persoonlike programme" + "Lys werkprogramme" "Tuis" "Verwyder" "Deïnstalleer" @@ -77,19 +83,17 @@ "Muurpapiere" "Home-instellings" "Gedeaktiveer deur jou administrateur" - "Oorsig" - "Laat toe dat tuisskerm gedraai word" - "Wanneer foon gedraai word" - "Huidige vertooninstelling laat nie rotasie toe nie" "Kennisgewingkolle" "Aan" "Af" "Kennisgewingtoegang word benodig" "Skakel programkennisgewings vir %1$s aan om kennisgewingkolle te sien" "Verander instellings" + "Wys kennisgewingkolle" "Voeg ikoon by tuisskerm" "Vir nuwe programme" "Verander ikoon se vorm" + "op tuisskerm" "Gebruik stelselverstek" "Vierkant" "Sirkelvierkant" @@ -119,9 +123,6 @@ "Skep vouer met: %1$s" "Vouer geskep" "Skuif na tuisskerm" - "Skuif skerm na links" - "Skuif skerm na regs" - "Skerm is geskuif" "Verander grootte" "Vermeerder breedte" "Vermeerder hoogte" @@ -137,8 +138,9 @@ "Werk" "Werkprofiel" "Kry werkprogramme hier" - - + "Elke werkprogram het \'n kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou tuisskerm toe vir makliker toegang." "Bestuur deur jou organisasie" "Kennisgewings en programme is af" + "Maak toe" + "Toe" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index d4afc535e4..b4b3c79174 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -40,9 +40,15 @@ "%1$s sorğusuna uyğun tətbiq tapılmadı" "Daha çox tətbiq üçün axtarış edin" "Bildirişlər" + + + + "Bu Əsas ekranda boş yer yoxdur." "Favoritlər-də yer yoxdur" "Tətbiq siyahısı" + "Şəxsi tətbiqlərin siyahısı" + "İş tətbiqlərinin siyahısı" "Əsas səhifə" "Silin" "Sistemdən sil" @@ -77,19 +83,17 @@ "Divar kağızları" "Home ayarları" "Admininiz tərəfindən deaktiv edilib" - "İcmal" - "Əsas ekranın firlanmağına icazə verin" - "Telefon çevrilən zaman" - "Cari Ekran ayarı fırlatmağa icazə vermir" "Bildiriş nişanı" "Aktiv" "Deaktiv" "Bildiriş girişi tələb edilir" "Bildiriş Nöqtələrini göstərmək üçün %1$s bildirişlərini aktiv edin" "Ayarları dəyişin" + "Bildiriş nöqtələrini göstərin" "Əsas ekrana ikona əlavə edin" "Yeni tətbiqlər üçün" "İkona formasını dəyişin" + "Əsas səhifə ekranında" "Sistem defoltu istifadə edin" "Kvadrat" "Kənarları dairəvi kvadrat" @@ -119,9 +123,6 @@ "Qovluq yaradın: %1$s" "Qovluq yaradıldı" "Əsas ekrana köçürün" - "Ekranı sola köçürün" - "Ekranı sağa köçürün" - "Ekran köçürülüb" "Ölçüsünü dəyişin" "Eni artırın" "Hündürlüyü artırın" @@ -137,8 +138,9 @@ "İş" "İş profili" "Burada iş tətbiqləri axtarın" - - + "Hər bir iş tətbiqində təşkilat tərəfindən qorunduğunu göstərən narıncı nişan var. Tətbiqləri daha asan giriş üçün Əsas Səhifə Ekranına köçürün." "Təşkilatınız tərəfindən idarə olunur" "Bildiriş və tətbiqlər deaktivdir" + "Bağlayın" + "Bağlıdır" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 113c6d114b..e664570527 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -40,9 +40,15 @@ "Няма намерени приложения, съответстващи на „%1$s“" "Търсене на още приложения" "Известия" + + + + "На този начален екран няма повече място." "Няма повече място в областта с любимите" "Списък с приложения" + "Списък с лични приложения" + "Списък със служебни приложения" "Начало" "Премахване" "Деинсталиране" @@ -77,19 +83,17 @@ "Тапети" "Настройки за Google Home" "Деактивирано от администратора ви" - "Общ преглед" - "Разрешаване на завъртането на началния екран" - "При завъртане на телефона" - "Текущата настройка на екрана не разрешава завъртане" "Точки за известия" "Включено" "Изключено" "Необходим е достъп до известията" "За да се показват точки за известия, включете известията за приложението %1$s" "Промяна на настройките" + "Показване на точките за известия" "Добавяне на икона към началния екран" "За нови приложения" "Промяна на формата на иконите" + "на началния екран" "Използване на стандартната системна настройка" "Квадрат" "Комбинация от кръг и квадрат" @@ -119,9 +123,6 @@ "Създаване на папка с елемента „%1$s“" "Папката е създадена" "Преместване към началния екран" - "Преместване на екрана наляво" - "Преместване на екрана надясно" - "Екранът е преместен" "Преоразмеряване" "Увеличаване на ширината" "Увеличаване на височината" @@ -137,8 +138,9 @@ "Служебни" "Служебен потребителски профил" "Тук можете да намерите служебните приложения" - - + "Всяко служебно приложение има значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран." "Управлява се от организацията ви" "Известията и приложенията са изключени" + "Затваряне" + "Затворено" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index ed57612ab5..46a9a39026 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি" "আরও অ্যাপ্লিকেশানের জন্য খুঁজুন" "বিজ্ঞপ্তি" + + + + "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" "অ্যাপ্লিকেশানগুলির তালিকা" + "ব্যক্তিগত অ্যাপের তালিকা" + "কাজের অ্যাপের তালিকা" "হোম" "সরান" "আনইনস্টল করুন" @@ -77,19 +83,17 @@ "ওয়ালপেপারগুলি" "হোম এর সেটিংস" "আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে" - "এক নজরে" - "হোমস্ক্রীন ঘোরানোর অনুমতি দিন" - "যখন ফোনটি ঘোরানো হয়" - "বর্তমান প্রদর্শনের সেটিংস ঘোরানোর মঞ্জুরি দেয় না" "বিজ্ঞপ্তি ডট" "চালু হয়েছে" "বন্ধ আছে" "বিজ্ঞপ্তিতে অ্যাক্সেস প্রয়োজন" "বিজ্ঞপ্তির ডটগুলি দেখানোর জন্য, %1$s এর অ্যাপ বিজ্ঞপ্তি চালু করুন" "সেটিংস পরিবর্তন করুন" + "বিজ্ঞপ্তির ডট দেখুন" "হোম স্ক্রিনে আইকন যোগ করুন" "নতুন অ্যাপ্লিকেশানগুলির জন্যে" "আইকনের আকৃতি পরিবর্তন করুন" + "হোম স্ক্রিনে" "সিস্টেমের ডিফল্ট মান ব্যবহার করুন" "চৌকো" "চৌকো-গোলাকার" @@ -119,9 +123,6 @@ "এর সাথে ফোল্ডার তৈরি করুন: %1$s" "ফোল্ডার তৈরি করা হয়েছে" "হোম স্ক্রীনে সরান" - "স্ক্রীন বাঁ দিকে সরান" - "স্ক্রীন ডান দিকে সরান" - "স্ক্রীন সরানো হয়েছে" "আবার আকার দিন" "প্রস্থ বাড়ান" "উচ্চতা বাড়ান" @@ -137,8 +138,11 @@ "অফিস" "অফিসের প্রোফাইল" "এখানে কাজের অ্যাপ্সগুলি খুঁজুন" - - + "প্রতিটি কাজের অ্যাপে একটি করে ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।" "আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত" "বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে" + + + + diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index b9ba069fcb..57736296ba 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -40,9 +40,15 @@ "No s\'ha trobat cap aplicació que coincideixi amb \"%1$s\"" "Cerca més aplicacions" "Notificacions" + + + + "Ja no queda espai en aquesta pantalla d\'inici." "No hi ha més espai a la safata Preferits." "Llista d\'aplicacions" + "Llista d\'aplicacions personals" + "Llista d\'aplicacions per a la feina" "Inici" "Suprimeix" "Desinstal·la" @@ -77,19 +83,17 @@ "Fons de pantalla" "Configuració de la pantalla d\'inici" "Desactivada per l\'administrador" - "Visió general" - "Permet la rotació de la pantalla d\'inici" - "En girar el telèfon" - "La configuració actual de la pantalla no permet la rotació" "Punts de notificació" "Activat" "Desactivat" "Cal que tingui accés a les notificacions" "Per veure els punts de notificació, activa les notificacions de l\'aplicació %1$s" "Canvia la configuració" + "Mostra els punts de notificació" "Afegeix la icona a la pantalla d\'inici" "Per a les aplicacions noves" "Canvia la forma de les icones" + "a la pantalla d\'inici" "Utilitza l\'opció predeterminada del sistema" "Quadrat" "Quadrat arrodonit" @@ -119,9 +123,6 @@ "Crea una carpeta amb: %1$s" "Carpeta creada" "Desplaça a la pantalla d\'inici" - "Desplaça pantalla a l\'esquerra" - "Desplaça pantalla a la dreta" - "S\'ha desplaçat la pantalla" "Canvia la mida" "Augmenta l\'amplada" "Augmenta l\'alçada" @@ -137,8 +138,9 @@ "Feina" "Perfil professional" "Cerca aplicacions per a la feina aquí" - - + "Totes les aplicacions per a la feina tenen una insígnia que indica que estan protegides per la teva organització. Mou les aplicacions a la pantalla d\'inici per poder-hi accedir més fàcilment." "Gestionat per la teva organització" "Les notificacions i les aplicacions estan desactivades" + "Tanca" + "Tancada" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 0aaedcab79..79fa9f12d2 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -43,6 +43,10 @@ "Der er ikke mere plads på denne startskærm." "Der er ikke mere plads i bakken Foretrukne" "Liste med apps" + + + + "Startskærm" "Fjern" "Afinstaller" @@ -77,19 +81,18 @@ "Baggrunde" "Indstillinger for startskærmen" "Deaktiveret af din administrator" - "Oversigt" - "Tillad rotation af startskærmen" - "Når telefonen roteres" - "Den aktuelle indstilling for visning tillader ikke rotation" "Underretningscirkler" "Til" "Fra" "Kræver adgang til underretninger" "Hvis du vil se underretningscirkler, skal du aktivere appunderretninger for %1$s" "Skift indstillinger" + "Vis underretningscirkler" "Føj ikon til startskærmen" "For nye apps" "Skift ikonform" + + "Brug systemstandarden" "Kvadrat" "Kvadrat med runde hjørner" @@ -119,9 +122,6 @@ "Opret mappe med: %1$s" "Mappen blev oprettet" "Flyt til startskærmen" - "Flyt skærmen til venstre" - "Flyt skærmen til højre" - "Skærmen er flyttet" "Tilpas størrelse" "Øg bredden" "Øg højden" @@ -137,8 +137,7 @@ "Arbejde" "Arbejdsprofil" "Find arbejdsapps her" - - + "Alle arbejdsapps har et badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem." "Administreret af din organisation" "Underretninger og apps er slået fra" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 21c9706f41..e139e44064 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -40,9 +40,15 @@ "Δεν βρέθηκαν εφαρμογές αντιστοίχισης για \"%1$s\"" "Αναζήτηση περισσότερων εφαρμογών" "Ειδοποιήσεις" + + + + "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." "Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα" "Λίστα εφαρμογών" + "Λίστα προσωπικών εφαρμογών" + "Λίστα εφαρμογών εργασίας" "Αρχική οθόνη" "Κατάργηση" "Απεγκατάσταση" @@ -77,19 +83,17 @@ "Ταπετσαρίες" "Ρυθμίσεις Αρχικής σελίδας" "Απενεργοποιήθηκε από τον διαχειριστή σας" - "Επισκόπηση" - "Να επιτρέπεται η περιστροφή της αρχικής οθόνης" - "Όταν το τηλέφωνο περιστρέφεται" - "Η τρέχουσα ρύθμιση οθόνης δεν επιτρέπει την περιστροφή" "Κουκκίδες ειδοποίησης" "Ενεργή" "Ανενεργή" "Απαιτείται πρόσβαση στις ειδοποιήσεις" "Για να εμφανιστούν οι Κουκκίδες ειδοποίησης, ενεργοποιήστε τις κουκκίδες εφαρμογής για την εφαρμογή %1$s" "Αλλαγή ρυθμίσεων" + "Εμφάνιση κουκκίδων ειδοποίησης" "Προσθήκη εικονιδίου στην Αρχική οθόνη" "Για νέες εφαρμογές" "Αλλαγή σχήματος εικονιδίου" + "στην Αρχική οθόνη" "Χρήση προεπιλογής συστήματος" "Τετράγωνο" "Στρογγυλεμένο τετράγωνο" @@ -119,9 +123,6 @@ "Δημιουργία φακέλου με: %1$s" "Δημιουργήθηκε φάκελος" "Μετακίνηση Αρχικής οθόνης" - "Μετακίνηση οθόνης αριστερά" - "Μετακίνηση οθόνης δεξιά" - "Η οθόνη μετακινήθηκε" "Προσαρμογή μεγέθους" "Αύξηση του πλάτους" "Αύξηση του ύψους" @@ -137,8 +138,9 @@ "Εργασίας" "Προφίλ εργασίας" "Βρείτε όλες τις εφαρμογές εργασίας εδώ" - - + "Κάθε εφαρμογή εργασίας φέρει ένα σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση." "Διαχειριζόμενο από τον οργανισμό σας" "Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες" + "Κλείσιμο" + "Κλειστή" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index c84efc786b..5b1e39adf7 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -40,9 +40,15 @@ "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" + + + + "No more room on this Home screen." "No more room in the Favourites tray" "Apps list" + "Personal apps list" + "Work apps list" "Home" "Remove" "Uninstall" @@ -77,19 +83,17 @@ "Wallpapers" "Home settings" "Disabled by your admin" - "Overview" - "Allow Homescreen rotation" - "When phone is rotated" - "Current display setting doesn\'t permit rotation" "Notification dots" "On" "Off" "Notification access needed" "To show Notification Dots, turn on app notifications for %1$s" "Change settings" + "Show notification dots" "Add icon to Home screen" "For new apps" "Change icon shape" + "on Home screen" "Use system default" "Square" "Squircle" @@ -119,9 +123,6 @@ "Create folder with: %1$s" "Folder created" "Move to Home screen" - "Move screen to left" - "Move screen to right" - "Screen moved" "Re-size" "Increase width" "Increase height" @@ -137,8 +138,9 @@ "Work" "Work profile" "Find work apps here" - - + "Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access." "Managed by your organisation" "Notifications and apps are off" + "Close" + "Closed" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index c84efc786b..5b1e39adf7 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -40,9 +40,15 @@ "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" + + + + "No more room on this Home screen." "No more room in the Favourites tray" "Apps list" + "Personal apps list" + "Work apps list" "Home" "Remove" "Uninstall" @@ -77,19 +83,17 @@ "Wallpapers" "Home settings" "Disabled by your admin" - "Overview" - "Allow Homescreen rotation" - "When phone is rotated" - "Current display setting doesn\'t permit rotation" "Notification dots" "On" "Off" "Notification access needed" "To show Notification Dots, turn on app notifications for %1$s" "Change settings" + "Show notification dots" "Add icon to Home screen" "For new apps" "Change icon shape" + "on Home screen" "Use system default" "Square" "Squircle" @@ -119,9 +123,6 @@ "Create folder with: %1$s" "Folder created" "Move to Home screen" - "Move screen to left" - "Move screen to right" - "Screen moved" "Re-size" "Increase width" "Increase height" @@ -137,8 +138,9 @@ "Work" "Work profile" "Find work apps here" - - + "Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access." "Managed by your organisation" "Notifications and apps are off" + "Close" + "Closed" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index c84efc786b..5b1e39adf7 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -40,9 +40,15 @@ "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" + + + + "No more room on this Home screen." "No more room in the Favourites tray" "Apps list" + "Personal apps list" + "Work apps list" "Home" "Remove" "Uninstall" @@ -77,19 +83,17 @@ "Wallpapers" "Home settings" "Disabled by your admin" - "Overview" - "Allow Homescreen rotation" - "When phone is rotated" - "Current display setting doesn\'t permit rotation" "Notification dots" "On" "Off" "Notification access needed" "To show Notification Dots, turn on app notifications for %1$s" "Change settings" + "Show notification dots" "Add icon to Home screen" "For new apps" "Change icon shape" + "on Home screen" "Use system default" "Square" "Squircle" @@ -119,9 +123,6 @@ "Create folder with: %1$s" "Folder created" "Move to Home screen" - "Move screen to left" - "Move screen to right" - "Screen moved" "Re-size" "Increase width" "Increase height" @@ -137,8 +138,9 @@ "Work" "Work profile" "Find work apps here" - - + "Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access." "Managed by your organisation" "Notifications and apps are off" + "Close" + "Closed" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 0fb401ab9d..f6337fcb2c 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -40,9 +40,15 @@ "No se han encontrado aplicaciones que contengan \"%1$s\"" "Buscar más aplicaciones" "Notificaciones" + + + + "No queda espacio en la pantalla de inicio." "La bandeja de favoritos está completa" "Lista de aplicaciones" + "Lista de aplicaciones personales" + "Lista de aplicaciones del trabajo" "Inicio" "Quitar" "Desinstalar" @@ -77,19 +83,17 @@ "Fondos de pantalla" "Ajustes de Home" "Inhabilitada por el administrador" - "Visión general" - "Permitir rotación de la pantalla de inicio" - "Al girar el teléfono" - "La configuración de pantalla actual no permite girar la pantalla" "Puntos de notificación" "Activada" "Desactivada" "Se necesita acceso a las notificaciones" "Para mostrar burbujas de notificación, activa las notificaciones de %1$s" "Cambiar ajustes" + "Mostrar burbujas de notificación" "Añadir icono a la pantalla de inicio" "Para aplicaciones nuevas" "Cambiar forma de los iconos" + "en la pantalla de inicio" "Usar opción predeterminada del sistema" "Cuadrado" "Cuadrado con esquinas redondeadas" @@ -119,9 +123,6 @@ "Crear carpeta con: %1$s" "Carpeta creada" "Mover a la pantalla de inicio" - "Mover pantalla a la izquierda" - "Mover pantalla a la derecha" - "Pantalla movida" "Modificar tamaño" "Aumentar ancho" "Aumentar altura" @@ -137,8 +138,9 @@ "Trabajo" "Perfil de trabajo" "Aplicaciones de trabajo" - - + "Cada aplicación de trabajo tiene una insignia y está protegida por tu organización. Mueve las aplicaciones a la pantalla de inicio para acceder a ellas más fácilmente." "Administrada por tu organización" "Las notificaciones y las aplicaciones están desactivadas" + "Cerrar" + "Cerrada" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 555f99708d..fb24c2ab0d 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -40,9 +40,15 @@ "Päringule „%1$s” ei vastanud ükski rakendus" "Otsi rohkem rakendusi" "Märguanded" + + + + "Sellel avaekraanil pole enam ruumi." "Salves Lemmikud pole rohkem ruumi" "Rakenduste loend" + "Isiklike rakenduste loend" + "Töörakenduste loend" "Avaekraan" "Eemalda" "Desinstalli" @@ -77,19 +83,17 @@ "Taustapildid" "Avalehe seaded" "Keelas administraator" - "Ülevaade" - "Luba avaekraani pööramine" - "Kui telefoni pööratakse" - "Praegune kuvaseade ei luba pööramist" "Märguandetäpid" "Sees" "Väljas" "Vaja on juurdepääsu märguannetele" "Märguandetäppide kuvamiseks lülitage sisse rakenduse %1$s märguanded" "Seadete muutmine" + "Kuva märguandetäpid" "Lisa ikoon avaekraanile" "Uute rakenduste puhul" "Ikooni kuju muutmine" + "avaekraanil" "Kasuta süsteemi vaikeseadet" "Ruut" "Ümarate nurkadega ruut" @@ -119,9 +123,6 @@ "Kausta loomine nimega %1$s" "Kaust on loodud" "Teisalda avaekraanile" - "Teisalda ekraan vasakule" - "Teisalda ekraan paremale" - "Ekraan teisaldati" "Muuda suurust" "Suurenda laiust" "Suurenda kõrgust" @@ -137,8 +138,9 @@ "Töö" "Tööprofiil" "Töörakendused leiate siit" - - + "Igal töörakendusel on märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda." "Haldab teie organisatsioon" "Märguanded ja rakendused on välja lülitatud" + "Sule" + "Suletud" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 80d705db0f..45aa3f77cd 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -40,9 +40,15 @@ "Ez da aurkitu \"%1$s\" bilaketaren emaitzarik" "Bilatu aplikazio gehiago" "Jakinarazpenak" + + + + "Hasierako pantaila honetan ez dago toki gehiago." "Ez dago toki gehiago Gogokoak erretiluan" "Aplikazioen zerrenda" + "Aplikazio pertsonalen zerrenda" + "Laneko aplikazioen zerrenda" "Hasiera" "Kendu" "Desinstalatu" @@ -77,19 +83,17 @@ "Horma-paperak" "Hasierako pantailaren ezarpenak" "Administratzaileak desgaitu du" - "Ikuspegi orokorra" - "Baimendu hasierako pantaila biratzea" - "Telefonoa biratzen denean" - "Uneko pantaila-ezarpenak ez du onartzen ikuspegia biratzea" "Jakinarazteko biribiltxoak" "Aktibatuta" "Desaktibatuta" "Jakinarazpenetarako sarbidea behar da" "Jakinarazteko biribiltxoak ikusteko, aktibatu %1$s aplikazioaren jakinarazpenak" "Aldatu ezarpenak" + "Erakutsi jakinarazteko biribiltxoak" "Gehitu ikonoa hasierako pantailan" "Aplikazio berrietan" "Aldatu ikonoaren forma" + "Hasierako pantailan" "Erabili sistemaren balio lehenetsiak" "Karratua" "Ertz biribilduko karratua" @@ -119,9 +123,6 @@ "Sortu karpeta %1$s elementuarekin" "Karpeta sortu da" "Eraman hasierako pantailara" - "Eraman pantaila ezkerrera" - "Eraman pantaila eskuinera" - "Mugitu da pantaila" "Aldatu tamaina" "Handitu zabalera" "Handitu altuera" @@ -137,8 +138,9 @@ "Lanekoak" "Laneko profila" "Hemen dituzu laneko aplikazioak" - - + "Laneko aplikazio bakoitzak bereizgarri bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara." "Erakundeak kudeatzen du" "Jakinarazpenak eta aplikazioak desaktibatuta daude" + "Itxi" + "Itxita" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 9560b66da7..0a7d455846 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -40,9 +40,15 @@ "%1$s ei palauttanut sovelluksia." "Hae lisää sovelluksia" "Ilmoitukset" + + + + "Tässä aloitusruudussa ei ole enää tilaa." "Suosikit-valikossa ei ole enää tilaa" "Sovellusluettelo" + "Omat sovellukset ‑luettelo" + "Työsovellusluettelo" "Aloitusruutu" "Poista" "Poista asennus" @@ -77,19 +83,17 @@ "Taustakuvat" "Kotiasetukset" "Järjestelmänvalvoja on poistanut toiminnon käytöstä." - "Yleiskatsaus" - "Salli aloitusnäytön kiertäminen" - "Kun puhelinta kierretään" - "Nykyiset näyttöasetukset eivät salli näytön kiertämistä." "Pistemerkit" "Käytössä" "Pois käytöstä" "Ilmoituksien käyttöoikeus tarvitaan" "%1$s tarvitsee ilmoitusten käyttöoikeuden, jotta pistemerkkejä voidaan näyttää." "Muuta asetuksia" + "Näytä ilmoituksista kertovat pistemerkit" "Lisää kuvake aloitusruutuun" "Uusille sovelluksille" "Muuta kuvakkeen muotoa" + "aloitusnäytöllä" "Käytä järjestelmän oletusarvoa" "Neliö" "Ympyräneliö" @@ -119,9 +123,6 @@ "Luo kansio, jossa on %1$s." "Kansio on luotu." "Siirrä aloitusnäytölle" - "Siirrä näyttöä vasemmalle" - "Siirrä näyttöä oikealle" - "Näyttö siirrettiin." "Muuta kokoa" "Lisää leveyttä" "Lisää korkeutta" @@ -137,8 +138,9 @@ "Työsovellukset" "Työprofiili" "Etsi työsovelluksia tästä" - - + "Kaikki työsovellukset on merkitty, ja organisaatiosi vastaa niiden suojaamisesta. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi." "Organisaatiosi hallinnoima" "Ilmoitukset ja sovellukset ovat poissa käytöstä" + "Sulje" + "Suljettu" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 219b76e5fe..9feb1bd3b6 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -40,9 +40,15 @@ "Non se atoparon aplicacións que coincidan con \"%1$s\"" "Buscar máis aplicacións" "Notificacións" + + + + "Non hai máis espazo nesta pantalla de inicio." "Non hai máis espazo na bandexa de favoritos" "Lista de aplicacións" + "Lista de aplicacións persoais" + "Lista de aplicacións de traballo" "Inicio" "Eliminar" "Desinstalar" @@ -77,19 +83,17 @@ "Fondos de pantalla" "Configuración de inicio" "Función desactivada polo administrador" - "Visión xeral" - "Permitir xirar a pantalla de inicio" - "Ao xirar o teléfono" - "A configuración de visualización actual non permite xirar a pantalla" "Puntos de notificacións" "Activado" "Desactivado" "Necesítase acceso ás notificacións" "Para que se mostren os puntos de notificacións, activa as notificacións da aplicación %1$s" "Cambiar configuración" + "Mostrar puntos de notificación" "Engadir icona á pantalla de inicio" "Para novas aplicacións" "Cambiar forma das iconas" + "na pantalla de inicio" "Usar valores predeterminados do sistema" "Cadrado" "Cadrado de bordos redondeados" @@ -119,9 +123,6 @@ "Crear cartafol con: %1$s" "Creouse o cartafol" "Mover á pantalla de inicio" - "Mover pantalla á esquerda" - "Mover pantalla á dereita" - "Moveuse a pantalla" "Cambiar tamaño" "Aumentar ancho" "Aumentar altura" @@ -137,8 +138,9 @@ "Traballo" "Perfil de traballo" "Buscar aplicacións do traballo aquí" - - + "As aplicacións do traballo teñen unha insignia e están protexidas pola túa organización. Traslada as aplicacións á pantalla de inicio para acceder a elas de forma máis fácil." "Perfil xestionado pola túa organización" "As notificacións e as aplicacións están desactivadas" + "Pechar" + "Pechado" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 68b4edd16c..53627180ab 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" "વધુ ઍપ્લિકેશનો શોધો" "નોટિફિકેશનો" + + + + "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." "મનપસંદ ટ્રે પર વધુ જગ્યા નથી" "ઍપ્લિકેશનોની સૂચિ" + "વ્યક્તિગત ઍપની સૂચિ" + "કાર્યસ્થળની ઍપની સૂચિ" "હોમ" "દૂર કરો" "અનઇન્સ્ટોલ કરો" @@ -77,19 +83,17 @@ "વૉલપેપર્સ" "હોમ સેટિંગ્સ" "તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ" - "ઝલક" - "હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો" - "જ્યારે ફોન ફેરવવામાં આવે ત્યારે" - "વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી" "સૂચના બિંદુઓ" "ચાલુ" "બંધ" "નોટિફિકેશનનો ઍક્સેસની જરૂરી છે" "નોટિફિકેશન માટેનું ચિહ્ન બતાવવા હેતુ, %1$s માટેની ઍપ્લિકેશન નોટિફિકેશન ચાલુ કરો" "સેટિંગ્સ બદલો" + "નોટિફિકેશન માટેનું ચિહ્ન બતાવો" "હોમ સ્ક્રીન પર આઇકન ઉમેરો" "નવી ઍપ્લિકેશનો માટે" "આઇકનનો આકાર બદલો" + "હોમ સ્ક્રીન પર" "સિસ્ટમ ડિફૉલ્ટનો ઉપયોગ કરો" "ચોરસ" "ચોરસ જેવું ગોળ" @@ -119,9 +123,6 @@ "આની સાથે ફોલ્ડર બનાવો: %1$s" "ફોલ્ડર બનાવ્યું" "હોમ સ્ક્રીન પર ખસેડો" - "સ્ક્રીનને ડાબી બાજુ ખસેડો" - "સ્ક્રીનને જમણી બાજુ ખસેડો" - "સ્ક્રીન ખસેડી" "આકાર બદલો" "પહોળાઈ વધારો" "ઊંચાઈ વધારો" @@ -137,8 +138,11 @@ "કાર્યાલયની ઍપ" "કાર્યાલયની પ્રોફાઇલ" "કાર્ય ઍપને અહીંથી મેળવો" - - + "દરેક કાર્ય ઍપ પાસે એક બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો." "તમારી સંસ્થા દ્વારા મેનેજ કરેલ" "નોટિફિકેશન અને ઍપ બંધ છે" + + + + diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index f69019649a..997918bca4 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -40,9 +40,15 @@ "Nem található alkalmazás a(z) „%1$s” lekérdezésre" "További alkalmazások keresése" "Értesítések" + + + + "Nincs több hely ezen a kezdőképernyőn." "Nincs több hely a Kedvencek tálcán" "Alkalmazások listája" + "Személyes alkalmazások listája" + "Munkahelyi alkalmazások listája" "Főoldal" "Törlés" "Eltávolítás" @@ -77,19 +83,17 @@ "Háttérképek" "A Home beállításai" "A rendszergazda letiltotta" - "Áttekintés" - "A kezdőképernyő elforgatásának engedélyezése" - "A telefon elforgatásakor" - "A jelenlegi kijelzőbeállítások nem teszik lehetővé az elforgatást" "Értesítési pöttyök" "Bekapcsolva" "Kikapcsolva" "Értesítésekhez való hozzáférésre van szükség" "Az értesítési pöttyök megjelenítéséhez kapcsolja be a(z) %1$s alkalmazás értesítéseit" "Beállítások módosítása" + "Értesítési pöttyök megjelenítése" "Ikon hozzáadása a kezdőképernyőhöz" "Új alkalmazásoknál" "Ikon formájának módosítása" + "a kezdőképernyőn" "Alapértelmezett érték használata" "Négyzet" "Squircle" @@ -119,9 +123,6 @@ "Mappa létrehozása a következővel: %1$s" "Mappa létrehozva" "Áthelyezés a kezdőképernyőre" - "Képernyő mozgatása balra" - "Képernyő mozgatása jobbra" - "Képernyő áthelyezve" "Átméretezés" "Szélesség növelése" "Magasság növelése" @@ -137,8 +138,9 @@ "Munkahelyi" "Munkaprofil" "Itt kereshet munkahelyi alkalmazásokat" - - + "A munkahelyi alkalmazásoknál jelvény található, és biztonságukról az Ön szervezete gondoskodik. A könnyebb hozzáférés érdekében helyezze át az alkalmazásokat a kezdőképernyőre." "Az Ön szervezete kezeli" "Az értesítések és az alkalmazások ki vannak kapcsolva" + "Bezárás" + "Bezárva" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 89e57e7019..29a15f5376 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -40,9 +40,15 @@ "Tidak ditemukan aplikasi yang cocok dengan \"%1$s\"" "Telusuri aplikasi lainnya" "Notifikasi" + + + + "Tidak ada ruang lagi pada layar Utama ini." "Tidak ada ruang tersisa di baki Favorit" "Daftar aplikasi" + "Daftar aplikasi pribadi" + "Daftar aplikasi kantor" "Layar Utama" "Hapus" "Uninstal" @@ -77,19 +83,17 @@ "Wallpaper" "Setelan layar Utama" "Dinonaktifkan oleh admin" - "Ringkasan" - "Izinkan layar Utama diputar" - "Saat ponsel diputar" - "Setelan Tampilan Saat Ini tidak memungkinkan putaran" "Titik notifikasi" "Aktif" "Nonaktif" "Perlu akses notifikasi" "Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk %1$s" "Ubah setelan" + "Tampilkan titik notifikasi" "Tambahkan ikon ke Layar utama" "Untuk aplikasi baru" "Ubah bentuk ikon" + "di layar Utama" "Gunakan default sistem" "Persegi" "Persegi bundar" @@ -119,9 +123,6 @@ "Buat folder dengan: %1$s" "Folder dibuat" "Pindahkan ke layar Utama" - "Pindahkan layar ke kiri" - "Pindahkan layar ke kanan" - "Layar dipindahkan" "Ubah ukuran" "Tambahi lebar" "Tambahi tinggi" @@ -137,8 +138,9 @@ "Kantor" "Profil kerja" "Temukan aplikasi kerja di sini" - - + "Setiap aplikasi kerja memiliki badge dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses." "Dikelola oleh organisasi" "Notifikasi dan aplikasi nonaktif" + "Tutup" + "Ditutup" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 8584629e2d..03f98aebd7 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -40,9 +40,15 @@ "Nessuna app trovata corrispondente a \"%1$s\"" "Cerca altre app" "Notifiche" + + + + "Spazio nella schermata Home esaurito." "Spazio esaurito nella barra dei Preferiti" "Elenco di app" + "Elenco di app personali" + "Elenco di app di lavoro" "Home page" "Rimuovi" "Disinstalla" @@ -77,19 +83,17 @@ "Sfondi" "Impostazioni Home" "Disattivata dall\'amministratore" - "Panoramica" - "Consenti rotazione della schermata Home" - "Con il telefono ruotato" - "L\'impostazione corrente del display non consente la rotazione" "Indicatori notifica" "Attiva" "Non attiva" "Accesso alle notifiche necessario" "Per mostrare gli indicatori di notifica, attiva le notifiche per l\'app %1$s" "Modifica impostazioni" + "Mostra indicatori di notifica" "Aggiungi icone alla schermata Home" "Per le nuove app" "Cambia la forma delle icone" + "nella schermata Home" "Usa impostazione predefinita di sistema" "Quadrato" "Supercerchio" @@ -119,9 +123,6 @@ "Crea cartella con: %1$s" "Cartella creata" "Sposta nella schermata Home" - "Sposta schermata a sinistra" - "Sposta schermata a destra" - "Schermata spostata" "Ridimensiona" "Aumenta larghezza" "Aumenta altezza" @@ -137,8 +138,9 @@ "Lavoro" "Profilo di lavoro" "Qui puoi trovare le tue app di lavoro" - - + "Ogni app di lavoro è contrassegnata da un badge e viene tenuta al sicuro dalla tua organizzazione. Sposta le app nella schermata Home per accedervi più facilmente." "Gestito dalla tua organizzazione" "Le notifiche e le app non sono attive" + "Chiudi" + "Chiusa" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 75db7d6b61..19a9d3cbcc 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -40,9 +40,15 @@ "「%1$s」に一致するアプリは見つかりませんでした" "他のアプリを検索" "通知" + + + + "このホーム画面に空きスペースがありません。" "お気に入りトレイに空きスペースがありません" "アプリのリスト" + "個人用アプリのリスト" + "仕事用アプリのリスト" "ホーム" "削除" "アンインストール" @@ -77,19 +83,17 @@ "壁紙" "ホームの設定" "管理者により無効にされています" - "概要" - "ホーム画面の回転を許可" - "スマートフォンが回転したとき" - "現在の [ディスプレイ] 設定では回転を使用できません" "通知ドット" "ON" "OFF" "通知へのアクセス権限が必要" "通知ドットを表示するには、「%1$s」のアプリ通知を ON にしてください" "設定を変更" + "通知ドットの表示" "ホーム画面にアイコンを追加" "新しいアプリをダウンロードしたとき" "アイコンの形の変更" + "ホーム画面上" "システムのデフォルトを使用" "スクエア" "スクワークル" @@ -119,9 +123,6 @@ "「%1$s」フォルダを作成" "フォルダを作成しました" "ホーム画面に移動" - "画面を左に移動" - "画面を右に移動" - "画面を移動しました" "サイズを変更" "幅を広くする" "高さを高くする" @@ -137,8 +138,9 @@ "仕事用" "仕事用プロファイル" "ここには仕事用アプリが表示されます" - - + "仕事用アプリにはバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。" "組織によって管理されています" "通知とアプリは OFF です" + "閉じる" + "終了" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 5f3d47fc68..c9d4d26cf9 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -40,9 +40,15 @@ "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" "მეტი აპის პოვნა" "შეტყობინებები" + + + + "ამ მთავარ ეკრანზე ადგილი აღარ არის." "რჩეულების თაროზე ადგილი არ არის" "აპების სია" + "პერსონალური აპების სია" + "სამსახურის აპების სია" "მთავარი" "ამოშლა" "დეინსტალაცია" @@ -77,19 +83,17 @@ "ფონები" "მთავარი გვერდის პარამეტრები" "გათიშულია თქვენი ადმინისტრატორის მიერ" - "მიმოხილვა" - "მთავარი ეკრანის შეტრიალების დაშვება" - "ტელეფონის შეტრიალებისას" - "ბრუნვა დაუშვებელია ჩვენების მიმდინარე პარამეტრებით" "შეტყობინების ნიშნულები" "ჩართული" "გამორთული" "საჭიროა შეტყობინებებზე წვდომა" "შეტყობინებათა ნიშნულების საჩვენებლად, ჩართეთ აპის შეტყობინებები %1$s-ისთვის" "პარამეტრების შეცვლა" + "შეტყობინების ნიშნულების ჩვენება" "ხატულას მთავარ ეკრანზე დამატება" "ახალი აპებისთვის" "ხატულას ფორმის შეცვლა" + "მთავარ ეკრანზე" "ნაგულისხმევი სისტემური პარამეტრების გამოყენება" "კვადრატი" "წრეკუთხედი" @@ -119,9 +123,6 @@ "საქაღალდის შექმნა ერთეულით: %1$s" "საქაღალდე შექმნილია" "მთავარ ეკრანზე გადატანა" - "ეკრანის გადატანა მარცხნივ" - "ეკრანის გადატანა მარჯვნით" - "ეკრანი გადაადგილდა" "ზომის შეცვლა" "სიგანის გაზრდა" "სიმაღლის გაზრდა" @@ -137,8 +138,9 @@ "სამსახური" "სამსახურის პროფილი" "აქ თავმოყრილია სამსახურის აპები" - - + "სამსახურის თითოეულ აპს აქვს ბეჯი, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ." "იმართება თქვენი ორგანიზაციის მიერ" "შეტყობინებები და აპები გამორთულია" + "დახურვა" + "დახურული" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 12da5052f1..8a05cc0919 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" сұрауына сәйкес келетін қолданбалар жоқ" "Қосымша қолданбалар іздеу" "Хабарландырулар" + + + + "Бұл Негізгі экранда орын қалмады." "Қалаулылар науасында орын қалмады" "Қолданбалар тізімі" + "Жеке қолданбалар тізімі" + "Жұмыс қолданбаларының тізімі" "Негізгі" "Жою" "Жою" @@ -77,19 +83,17 @@ "Тұсқағаздар" "Негізгі экран параметрлері" "Әкімші өшірді" - "Шолу" - "Негізгі экранның бұрылуына рұқсат ету" - "Телефон бұрылғанда" - "Экранның ағымдағы параметрі айналуға рұқсат бермейді" "Хабарландыру белгілері" "Қосулы" "Өшірулі" "Хабарландыруға кіру рұқсаты қажет" "Хабарландыру белгілерін көрсету үшін %1$s қолданбасының қолданба хабарландыруларын қосыңыз" "Параметрлерді өзгерту" + "Хабарландыру белгілерін көрсету" "Негізгі экранға белгіше енгізу" "Жаңа қолданбаларға арналған" "Белгіше пішінін өзгерту" + "Негізгі экранда" "Жүйенің әдепкі параметрін пайдалану" "Шаршы" "Жұмыр төртбұрыш" @@ -119,9 +123,6 @@ "Мына бар қалтаны жасау: %1$s" "Қалта жасалды" "Негізгі экранға жылжыту" - "Экранды солға жылжыту" - "Экранды оңға жылжыту" - "Экран жылжытылды" "Өлшемін өзгерту" "Енін арттыру" "Биіктігін арттыру" @@ -137,8 +138,9 @@ "Жұмыс" "Жұмыс профилі" "Жұмыс қолданбалары осы жерде берілген" - - + "Әрбір жұмыс қолданбасында танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады." "Ұйым арқылы басқарылады" "Хабарландырулар мен қолданбалар өшірулі" + "Жабу" + "Жабық" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 9704194afc..9b7fa87c23 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -40,9 +40,15 @@ "រកមិនឃើញកម្មវិធី​ដែលត្រូវគ្នាជាមួយ \"%1$s\" ទេ" "ស្វែងរកកម្មវិធីច្រើនទៀត" "ការ​ជូនដំណឹង" + + + + "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" "គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ" "បញ្ជីកម្មវិធី" + "បញ្ជី​កម្មវិធី​ផ្ទាល់ខ្លួន" + "បញ្ជី​កម្មវិធី​ការងារ" "ដើម" "យកចេញ" "លុបការដំឡើង" @@ -77,19 +83,17 @@ "ផ្ទាំង​រូបភាព" "ការកំណត់​ទំព័រដើម" "បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក" - "សង្ខេប" - "អនុញ្ញាតការបងិ្វលអេក្រង់ដើម" - "នៅពេលដែលបង្វិលទូរស័ព្ទរបស់អ្នក" - "ការកំណត់អេក្រង់បច្ចុប្បន្នមិនអនុញ្ញាតការបង្វិលទេ" "ស្លាកជូនដំណឹង" "បើក" "បិទ" "តម្រូវ​ឲ្យមាន​សិទ្ធិចូល​ប្រើប្រាស់​ការជូនដំណឹង" "ដើម្បីបង្ហាញស្លាកជូនដំណឹង សូមបើកការជូនដំណឹងកម្មវិធីសម្រាប់ %1$s" "ប្ដូរ​ការកំណត់" + "បង្ហាញ​ស្លាក​ជូនដំណឹង" "បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម" "សម្រាប់កម្មវិធីថ្មី" "ប្តូររូបរាងរូបតំណាង" + "នៅ​លើ​អេក្រង់​ដើម" "ប្រើលំនាំដើមរបស់ប្រព័ន្ធ" "ការ៉េ" "ការ៉េជ្រុងកោង" @@ -119,9 +123,6 @@ "បង្កើតថតឯកសារជាមួយ៖ %1$s" "បានបង្កើតថតឯកសារ" "ផ្លាស់ទៅអេក្រង់ដើម" - "រំកិលអេក្រង់ទៅខាងឆ្វេង" - "រំកិលអេក្រង់ទៅខាងស្តាំ" - "អេក្រង់ដែលបានផ្លាស់ទី" "ប្ដូរទំហំ" "បង្កើនទទឹង" "បង្កើនកម្ពស់" @@ -137,8 +138,9 @@ "ការងារ" "កម្រងព័ត៌មានការងារ" "ស្វែង​រក​កម្មវិធី​ការងារ​នៅទីនេះ" - - + "កម្មវិធី​ការងារ​នីមួយៗ​មាន​ស្លាកមួយ និងត្រូវ​បានរក្សាទុក​យ៉ាងមានសុវត្ថិភាព​ដោយស្ថាប័ន​របស់អ្នក។ សូម​ផ្លាស់ទី​កម្មវិធី​ទៅកាន់​អេក្រង់​ដើម​របស់អ្នក​ ដើម្បី​ងាយស្រួល​ចូលប្រើជាងមុន។" "គ្រប់គ្រងដោយ​ស្ថាប័ន​របស់អ្នក" "ការជូនដំណឹង​ និងកម្មវិធី​ត្រូវបានបិទ" + "បិទ" + "បានបិទ" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 1746cbb282..0e314bb121 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" "ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" "ಅಧಿಸೂಚನೆಗಳು" + + + + "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." "ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ" + "ವೈಯಕ್ತಿಕ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ" + "ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ" "ಮುಖಪುಟ" "ತೆಗೆದುಹಾಕಿ" "ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್" @@ -77,19 +83,17 @@ "ವಾಲ್‌ಪೇಪರ್‌ಗಳು" "ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್‌ಗಳು" "ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ" - "ಅವಲೋಕನ" - "ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ" - "ಫೋನ್‌ ತಿರುಗಿಸಿದಾಗ" - "ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಸೆಟ್ಟಿಂಗ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ" "ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು" "ಆನ್" "ಆಫ್" "ಅಧಿಸೂಚನೆ ಪ್ರವೇಶ ಅಗತ್ಯವಿದೆ" "ಅಧಿಸೂಚನೆ ಚುಕ್ಕೆಗಳನ್ನು ತೋರಿಸಲು, %1$s ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್‌ ಮಾಡಿ" "ಸೆಟ್ಟಿಂಗ್‌‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ" + "ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳನ್ನು ತೋರಿಸಿ" "ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ" "ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ" "ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ" + "ಮುಖಪುಟ ಪರದೆಯಲ್ಲಿ" "ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ" "ಚೌಕ" "ಚೌಕವೃತ್ತ" @@ -119,9 +123,6 @@ "ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಲ್ಡರ್ ರಚಿಸಿ: %1$s" "ಫೋಲ್ಡರ್ ರಚಿಸಲಾಗಿದೆ" "ಮುಖಪುಟಕ್ಕೆ ಸರಿಸಿ" - "ಪರದೆಯನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ" - "ಪರದೆಯನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ" - "ಪರದೆ ಸರಿಸಲಾಗಿದೆ" "ಮರುಗಾತ್ರ" "ಅಗಲವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ" "ಎತ್ತರವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ" @@ -137,8 +138,11 @@ "ಕೆಲಸ" "ಕೆಲಸದ ಪ್ರೊಫೈಲ್" "ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಹುಡುಕಿ" - - + "ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸರಿಸಿ." "ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ" "ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಆಫ್ ಆಗಿವೆ" + + + + diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 580297fa50..60c3e4f6b4 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -40,9 +40,15 @@ "\'%1$s\'과(와) 일치하는 앱이 없습니다." "더 많은 앱 검색" "알림" + + + + "홈 화면에 더 이상 공간이 없습니다." "즐겨찾기 트레이에 더 이상 공간이 없습니다." "앱 목록" + "개인 앱 목록" + "업무용 앱 목록" "홈" "삭제" "제거" @@ -77,19 +83,17 @@ "배경화면" "홈 설정" "관리자가 사용 중지함" - "개요" - "홈 화면 회전 허용" - "휴대전화 회전 시" - "현재 표시 설정에는 회전 기능이 허용되지 않습니다." "알림 표시 점" "사용" "사용 안함" "알림 액세스 권한 필요" "알림 표시점을 표시하려면 %1$s의 앱 알림을 사용 설정하세요." "설정 변경" + "알림 표시 점 보기" "홈 화면에 아이콘 추가" "새로 설치한 앱에 적용" "아이콘 모양 변경" + "홈 화면에 표시" "시스템 기본값 사용" "정사각형" "모서리가 둥근 정사각형" @@ -119,9 +123,6 @@ "다음이 포함된 폴더 만들기: %1$s" "폴더를 만들었습니다." "홈 화면으로 이동" - "화면을 왼쪽으로 이동" - "화면을 오른쪽으로 이동" - "화면 이동됨" "크기 조정" "폭 늘리기" "높이 늘리기" @@ -137,8 +138,9 @@ "직장" "직장 프로필" "여기에서 업무용 앱 찾기" - - + "각 업무용 앱에는 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요." "조직에서 관리" "알림 및 앱 사용 중지됨" + "닫기" + "종료됨" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index e6493dbc41..84bee66e31 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" сурамына дал келген колдонмолор табылган жок" "Көбүрөөк колдонмолорду издөө" "Эскертмелер" + + + + "Бул Үй экранында бош орун жок." "Тандамалдар тайпасында орун калган жок" "Колдонмолор тизмеси" + "Жеке колдономолордун тизмеси" + "Жумуш колдонмолорунун тизмеси" "Үйгө" "Алып салуу" "Чыгарып салуу" @@ -77,19 +83,17 @@ "Тушкагаздар" "Башкы беттин жөндөөлөрү" "Администраторуңуз өчүрүп койгон" - "Көз жүгүртүү" - "Башкы экранды айлантууга уруксат берүү" - "Телефон айланганда" - "Экранды айлантуу параметри өчүрүлгөн" "Эскертме белгилери" "Күйүк" "Өчүк" "Эскертмелерге уруксат берилиши керек" "Эскертме белгилерин көрсөтүү максатында, %1$s үчүн колдонмонун эскертмелерин күйгүзүү керек" "Жөндөөлөрдү өзгөртүү" + "Эскертме белгилерин көрсөтүү" "Башкы экранга сүрөтчө кошуу" "Жаңы колдонмолор үчүн" "Сүрөтчөнүн формасын өзгөртүү" + "Башкы экранда" "Тутум сушунтаган демейкисин колдонуу" "Чарчы" "Бурчтары жумуру төрт бурчтук" @@ -119,9 +123,6 @@ "Төмөнкү менен куржун түзүү: %1$s" "Куржун түзүлдү" "Башкы экранга жылдыруу" - "Экранды солго жылдыруу" - "Экранды оңго жылдыруу" - "Экран жылдырылды" "Өлчөмүн өзгөртүү" "Кеңейтүү" "Бийиктетүү" @@ -137,8 +138,9 @@ "Жумуш колдонмолору" "Жумуш профили" "Жумуш колдонмолорун бул жерден таап алыңыз" - - + "Ар бир жумуш колдонмосунун бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз." "Уюмуңуз тарабынан башкарылат" "Билдирүүлөр жана колдонмолор өчүрүлгөн" + "Жабуу" + "Жабык" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index c0c9bb0702..bd89e6e926 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -40,9 +40,15 @@ "ບໍ່ພົບແອັບທີ່ກົງກັບ \"%1$s\"" "ຊອກຫາແອັບເພີ່ມເຕີມ" "ການແຈ້ງເຕືອນ" + + + + "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." "ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ" "ລາຍຊື່ແອັບ" + "ລາຍຊື່ແອັບສ່ວນຕົວ" + "ລາຍຊື່ແອັບເຮັດວຽກ" "ໜ້າຫຼັກ" "ເອົາ​ອອກ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" @@ -77,19 +83,17 @@ "ພາບພື້ນຫຼັງ" "ການຕັ້ງຄ່າ Home" "ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ" - "ພາບຮວມ" - "ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້" - "ເມື່ອໝຸນໂທລະສັບ" - "ການຕັ້ງຄ່າສະແດງຜົນປັດຈຸບັນບໍ່ອະນຸຍາດໃຫ້ໝຸນໄດ້" "ຈຸດການແຈ້ງເຕືອນ" "ເປີດ" "ປິດ" "ຕ້ອງໃຊ້ການເຂົ້າເຖິງການແຈ້ງເຕືອນ" "ເພື່ອສະແດງຈຸດການແຈ້ງເຕືອນ, ໃຫ້ເປີດການແຈ້ງເຕືອນສຳລັບ %1$s" "ບັນທຶກການຕັ້ງຄ່າ" + "ສະແດງຈຸດການແຈ້ງເຕືອນ" "ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ" "ສຳລັບແອັບໃໝ່" "ປ່ຽນຮູບຮ່າງໄອຄອນ" + "ຢູ່ໜ້າຈໍຫຼັກ" "ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ" "ສີ່ຫຼ່ຽມຈັດຕຸລັດ" "ສີ່ຫຼ່ຽມຂອບມົນ" @@ -119,9 +123,6 @@ "ສ້າງ​ໂຟ​ລ​ເດີ​ກັບ: %1$s" "ສ້າງ​ໂຟ​ລ​ເດີ​ແລ້ວ" "ຍ້າຍ​ໄປ​ໃສ່​ໜ້າ​ຈໍ​ຫຼັກ" - "ຍ້າຍ​ໜ້າ​ຈໍ​ໄປ​ທາງ​ຊ້າຍ" - "ຍ້າຍ​ໜ້າ​ຈໍ​ໄປ​ທາງ​ຂວາ" - "ຍ້າຍ​ໜ້າ​ຈໍ​ແລ້ວ" "ປັບຂະໜາດ" "ເພີ່ມ​ລວງ​ກ້​ວາງ​ຂຶ້ນ" "ເພີ່ມ​ລວງ​ສູງ​ຂຶ້ນ" @@ -137,8 +138,9 @@ "ວຽກ" "ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ" "ຊອກຫາແອັບວຽກຢູ່ບ່ອນນີ້" - - + "ແຕ່ລະແອັບວຽກຈະມີປ້າຍ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້." "ຈັດການໂດຍອົງກອນຂອງທ່ານ" "ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້" + "ປິດ" + "ປິດແລ້ວ" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 01408ddd9f..9eedfeb881 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -40,9 +40,15 @@ "Не се најдени апликации што одговараат на „%1$s“" "Пребарај други апликации" "Известувања" + + + + "Нема повеќе простор на овој екран на почетната страница." "Нема повеќе простор на лентата „Омилени“" "Список со апликации" + "Список со лични апликации" + "Список со апликации за работа" "Почетна страница" "Отстрани" "Деинсталирај" @@ -77,19 +83,17 @@ "Тапети" "Поставки за Home" "Оневозможено од администраторот" - "Краток преглед" - "Дозволете ротација на Почетниот екран" - "Кога телефонот се ротира" - "Тековната поставка на Екранот не дозволува ротација" "Точки за известување" "Вклучено" "Исклучено" "Потребен е пристап до известувањата" "За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата %1$s" "Промени ги поставките" + "Прикажи точки за известување" "Додај икона на почетниот екран" "За нови апликации" "Променете ја формата на иконата" + "на „Почетен екран“" "Користи ја стандардната поставка на системот" "Квадрат" "Заоблен квадрат" @@ -119,9 +123,6 @@ "Создај папка со: %1$s" "Папката е создадена" "Премести на Почетен екран" - "Движи го екранот налево" - "Движи го екранот надесно" - "Екранот е преместен" "Промени големина" "Зголеми ширина" "Зголеми висина" @@ -137,8 +138,9 @@ "За работа" "Работен профил" "Најдете апликации за работа тука" - - + "Секоја апликација за работа има значка, а организацијата се грижи за нејзината безбедност. За полесен пристап, преместете ги апликациите на почетниот екран." "Управувано од вашата организација" "Известувањата и апликациите се исклучени" + "Затвори" + "Затворено" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 6e6340b5a0..9455688038 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല" "കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക" "അറിയിപ്പുകൾ" + + + + "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." "പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല" "അപ്ലിക്കേഷനുകളുടെ ലിസ്‌റ്റ്" + "വ്യക്തിഗത ആപ്പുകളുടെ ലിസ്റ്റ്" + "ഔദ്യോഗിക ആപ്പുകളുടെ ലിസ്റ്റ്" "ഹോം" "നീക്കംചെയ്യുക" "അൺഇൻസ്റ്റാളുചെയ്യുക" @@ -77,19 +83,17 @@ "വാൾപേപ്പർ" "ഹോം ക്രമീകരണം" "അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "അവലോകനം" - "ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക" - "ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ" - "നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല" "അറിയിപ്പ് ഡോട്ടുകൾ" "ഓൺ" "ഓഫ്" "അറിയിപ്പിനായുള്ള ആക്‌സസ് ആവശ്യമാണ്" "അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുന്നതിന്, %1$s എന്നയാളിനായുള്ള ആപ്പ് അറിയിപ്പുകൾ ഓണാക്കുക" "ക്രമീകരണം മാറ്റുക" + "അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുക" "ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക" "പുതിയ ആപ്പുകൾക്ക്" "ഐക്കണിന്റെ ആകാരം മാറ്റുക" + "ഹോം സ്‌ക്രീനിൽ" "സിസ്‌റ്റം ഡിഫോൾട്ട് ഉപയോഗിക്കുക" "ചതുരം" "ചതുരവൃത്തം" @@ -119,9 +123,6 @@ "ഇതുപയോഗിച്ച് ഫോൾഡർ സൃഷ്‌ടിക്കുക: %1$s" "ഫോൾഡർ സൃഷ്‌ടിച്ചു" "ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക" - "സ്‌ക്രീൻ ഇടത്തേക്ക് നീക്കുക" - "സ്‌ക്രീൻ വലത്തേക്ക് നീക്കുക" - "സ്‌ക്രീൻ നീക്കി" "വലുപ്പംമാറ്റുക" "വീതി കൂട്ടുക" "ഉയരം കൂട്ടുക" @@ -137,8 +138,11 @@ "ജോലി" "ഔദ്യോഗിക പ്രൊഫൈൽ" "ഔദ്യോഗിക ആപ്പുകൾ ഇവിടെ കണ്ടെത്തുക" - - + "എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഒരു ബാഡ്‌ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക." "നിങ്ങളുടെ സ്ഥാപനം നിയന്ത്രിക്കുന്നത്" "അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്" + + + + diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 7119ccf700..7d0c0ef2ca 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\"-д тохирох апп олдсонгүй" "Бусад апп-г хайх" "Мэдэгдэл" + + + + "Энэ Нүүр дэлгэц зайгүй." "\"Дуртай\" трей дээр өөр зай байхгүй байна" "Апп-н жагсаалт" + "Хувийн аппын жагсаалт" + "Ажлын аппын жагсаалт" "Нүүр" "Арилгах" "Устгах" @@ -77,19 +83,17 @@ "Ханын зураг" "Нүүр хуудасны тохиргоо" "Таны админ идэвхгүй болгосон" - "Тойм" - "Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх" - "Утсыг эргүүлсэн үед" - "Дэлгэцийн одоогийн тохиргоогоор эргүүлэх боломжгүй" "Мэдэгдлийн цэг" "Асаалттай" "Унтраалттай" "Мэдэгдлийн хандалт шаардлагатай" "Мэдэгдлийн цэгийг харуулахын тулд %1$s-д аппын мэдэгдлийг асаана уу" "Тохиргоог өөрчлөх" + "Мэдэгдлийн цэгийг харуулах" "Нүүр хуудаст дүрс тэмдэг нэмэх" "Шинэ аппад зориулсан" "Дүрс тэмдгийн хэлбэрийг өөрчлөх" + "Үндсэн нүүр хэсэгт" "Системийн өгөгдмөл тохиргоог ашиглах" "Дөрвөлжин" "Мохоо өнцөгтэй дөрвөлжин" @@ -119,9 +123,6 @@ "Хавтсыг: %1$s нэрээр үүсгэ" "Үүсгэсэн хавтас" "Нүүр дэлгэц рүү зөөх" - "Дэлгэцийг зүүн тийш зөөх" - "Дэлгэцийг баруун тийш зөөх" - "Дэлгэцийг зөөсөн" "Хэмжээг өөрчлөх" "Өргөсгөх" "Өндөрсгөх" @@ -137,8 +138,9 @@ "Ажил" "Ажлын профайл" "Ажлын аппыг эндээс олно уу" - - + "Ажлын апп тус бүр тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбар хандахын тулд тэдгээрийг Үндсэн нүүр хэсэгт зөөнө үү." "Танай байгууллагаас удирддаг" "Мэдэгдэл, апп унтраалттай байна" + "Хаах" + "Хаасан" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index fd5d999195..d843d37082 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" "अधिक अॅप्स शोधा" "सूचना" + + + + "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" "अॅप्स सूची" + "वैयक्तिक अॅप्स सूची" + "कामाच्या ठिकाणी वापरली जाणाऱ्या अॅप्सची सूची" "होम" "काढा" "अनइंस्टॉल करा" @@ -77,19 +83,17 @@ "वॉलपेपर" "होम सेटिंग्‍ज" "आपल्या प्रशासकाने अक्षम केले" - "अवलोकन" - "मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या" - "फोन फिरविला जातो तेव्हा" - "वर्तमान डिस्प्ले सेटिंग रोटेशनला परवानगी देत नाही" "सूचना बिंदू" "चालू" "बंद" "सूचनांच्या अ‍ॅक्सेसची आवश्यकता आहे" "सूचना बिंदू दाखवण्यासाठी, %1$s साठी अ‍ॅप सूचना चालू करा" "सेटिंग्ज बदला" + "सूचना बिंदू दाखवा" "होम स्क्रीनवर आयकन जोडा" "नवीन अॅप्ससाठी" "चिन्हाचा आकार बदला" + "होम स्क्रीनवर" "सिस्‍टमचे डीफॉल्‍ट वापरा" "चौरस" "गोलाकार चौरस" @@ -119,9 +123,6 @@ "यासह फोल्‍डर तयार करा: %1$s" "फोल्‍डर तयार केले" "मुख्य स्क्रीनवर हलवा" - "स्क्रीन डावीकडे हलवा" - "स्क्रीन उजवीकडे हलवा" - "स्क्रीन हलविली" "आकार बदला" "रूंदी वाढवा" "उंची वाढवा" @@ -137,8 +138,11 @@ "कार्यालय" "कार्य प्रोफाइल" "कामाची अ‍ॅप्स येथे मिळवा" - - + "प्रत्येक कार्य अ‍ॅपला एक बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अ‍ॅक्सेससाठी अ‍ॅप्स तुमच्या होम स्क्रीनवर हलवा." "तुमच्या संस्थेकडून व्यवस्थापित" "सूचना आणि अॅप्स बंद आहेत" + + + + diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 1fe38a6793..d403de0629 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -40,9 +40,15 @@ "Tiada apl yang ditemui sepadan dengan \"%1$s\"" "Cari lagi apl" "Pemberitahuan" + + + + "Tiada lagi ruang pada skrin Laman Utama ini." "Tiada ruang dalam dulang Kegemaran lagi" "Senarai apl" + "Senarai apl peribadi" + "Senarai apl kerja" "Laman Utama" "Alih keluar" "Nyahpasang" @@ -77,19 +83,17 @@ "Kertas dinding" "Tetapan laman utama" "Dilumpuhkan oleh pentadbir anda" - "Ikhtisar" - "Benarkan putaran Skrin Utama" - "Apabila telefon diputar" - "Tetapan Paparan semasa tidak membenarkan putaran" "Titik pemberitahuan" "Hidup" "Mati" "Akses pemberitahuan diperlukan" "Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk %1$s" "Tukar tetapan" + "Tunjukkan titik pemberitahuan" "Tambahkan ikon pada Skrin Utama" "Untuk apl baharu" "Tukar bentuk ikon" + "pada Skrin Utama" "Gunakan lalai sistem" "Segi empat sama" "Segi empat berbucu bulat" @@ -119,9 +123,6 @@ "Buat folder dengan: %1$s" "Folder dibuat" "Alihkan ke Skrin Utama" - "Alihkan skrin ke kiri" - "Alihkan skrin ke kanan" - "Skrin dialihkan" "Ubah saiz" "Tambahkan kelebaran" "Tambahkan ketinggian" @@ -137,8 +138,9 @@ "Kerja" "Profil kerja" "Temui apl kerja di sini" - - + "Setiap apl kerja terdapat lencana dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebih mudah." "Diurus oleh organisasi anda" "Pemberitahuan dan apl dimatikan" + "Tutup" + "Ditutup" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index c004318377..3c2d91f572 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ" "နောက်ထပ် အက်ပ်များကို ရှာပါ" "အကြောင်းကြားချက်များ" + + + + "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" "အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ" "အက်ပ်စာရင်း" + "တစ်ကိုယ်ရေသုံး အက်ပ်စာရင်း" + "အလုပ်သုံး အက်ပ်စာရင်း" "ပင်မစာမျက်နှာ" "ဖယ်ရှားမည်" "ဖယ်ထုတ်မည်" @@ -77,19 +83,17 @@ "နောက်ခံများ" "ပင်မဆက်တင်များ" "သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်" - "ခြုံငုံသုံးသပ်ချက်" - "ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ" - "ဖုန်းကိုလှည့်ထားစဉ်" - "လက်ရှိ မြင်ကွင်းဆက်တင်တွင် မြင်ကွင်းကို လှည့်ခွင့်မပေးပါ" "အကြောင်းကြားချက်အမှတ်အသားများ" "ဖွင့်ထားသည်" "ပိတ်ထားသည်" "အကြောင်းကြားချက် အသုံးပြုခွင့် လိုအပ်သည်" "အကြောင်းကြားချက် အစက်များကို ပြသရန် %1$s အတွက် အက်ပ်အကြောင်းကြားချက်များကို ဖွင့်ပါ" "ဆက်တင်များ ပြောင်းရန်" + "အကြောင်းကြားချက် အမှတ်အသားများကို ပြရန်" "ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်" "အက်ပ်အသစ်များအတွက်" "သင်္ကေတပုံစံကို ပြောင်းရန်" + "\'ပင်မမျက်နှာပြင်\' ပေါ်တွင်" "စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်" "လေးထောင့်" "စတုရန်းမကျ စက်ဝိုင်းမကျပုံ" @@ -119,9 +123,6 @@ "ဖိုလ်ဒါ ပြုလုပ်ရန်- %1$s" "ဖိုလ်ဒါ ပြုလုပ်ပြီး" "ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ" - "မျက်နှာပြင် ဘယ်ဘက်သို့ ရွှေ့ပါ" - "မျက်နှာပြင် ညာဘက်သို့ ရွှေ့ပါ" - "ဖန်မျက်နှာပြင် ပြောင်းရွှေ့ပြီး၏" "အရွယ်အစားပြောင်းပါ" "အကျယ်အား တိုးပါ" "အမြင့်အား တိုးပါ" @@ -137,8 +138,9 @@ "အလုပ်" "အလုပ်ပရိုဖိုင်" "အလုပ်အက်ပ်များကို ဤနေရာတွင်ရှာဖွေပါ" - - + "အလုပ်အက်ပ်တိုင်းတွင် တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။" "သင်၏ အဖွဲ့အစည်းက စီမံခန့်ခွဲထားပါသည်" "အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်" + "ပိတ်ရန်" + "ပိတ်ထားသည်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 7a36062744..f7cac37dad 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -40,9 +40,15 @@ "Fant ingen apper som samsvarer med «%1$s»" "Søk etter flere apper" "Varsler" + + + + "Denne startsiden er full." "Favoritter-skuffen er full" "App-liste" + "Personlige apper-liste" + "Jobbapper-liste" "Startside" "Fjern" "Avinstaller" @@ -77,19 +83,17 @@ "Bakgrunner" "Startsideinnstillinger" "Administratoren har slått av funksjonen" - "Oversikt" - "Tillat rotasjon av startskjermen" - "Når telefonen roteres" - "Med den nåværende skjerminnstillingen støttes ikke rotasjon" "Varselsprikker" "På" "Av" "Tilgang til varsler er nødvendig" "Slå på appvarsler for %1$s for å vise varselsprikker" "Endre innstillingene" + "Vis varselsprikker" "Legg til ikon på startsiden" "For nye apper" "Endre formen på ikonet" + "på startskjermen" "Bruk systemstandard" "Kvadrat" "Superellipse" @@ -119,9 +123,6 @@ "Opprett mappe med: %1$s" "Mappen er opprettet" "Flytt til startskjermen" - "Flytt skjermen til venstre" - "Flytt skjermen til høyre" - "Skjermen er flyttet" "Endre størrelse" "Øk bredden" "Øk høyden" @@ -137,8 +138,9 @@ "Jobb" "Jobbprofil" "Finn jobbapper her" - - + "Alle jobbapper har et merke og sikres av organisasjonen din. Flytt apper til startskjermen for å gjøre det enklere å finne dem." "Administreres av organisasjonen din" "Varsler og apper er slått av" + "Lukk" + "Lukket" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 602b162458..e7990c88b7 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोग भेटिएन" "थप अनुप्रयोगहरू खोज्नुहोस्" "सूचनाहरू" + + + + "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" "मनपर्ने ट्रे अब कुनै ठाँउ छैन" "अनुप्रयोगको सूची" + "व्यक्तिगत अनुप्रयोगहरूको सूची" + "कार्यसम्बन्धी अनुप्रयोगहरूको सूची" "गृह" "हटाउनुहोस्" "विस्थापित गर्नुहोस्" @@ -77,19 +83,17 @@ "वालपेपरहरु" "गृहपृष्ठका सेटिङहरू" "तपाईँको प्रशासकद्वारा असक्षम गरिएको" - "परिदृश्य" - "गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्" - "फोनलाई घुमाइँदा" - "हालको प्रदर्शन सम्बन्धी सेटिङले घुमाउने सुविधालाई अनुमति दिँदैन" "सूचनाको प्रतीक जनाउने थोप्लोहरू" "सक्रिय छ" "निष्क्रिय छ" "सूचनासम्बन्धी पहुँच आवश्यक हुन्छ" "सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउन %1$s को अनुप्रयोगसम्बन्धी सूचनाहरूलाई सक्रिय गर्नुहोस्" "सेटिङहरू बदल्नुहोस्" + "सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्" "गृह स्क्रिनमा आइकन थप्नुहोस्" "नयाँ अनुप्रयोगका लागि" "आइकनको आकार परिवर्तन गर्नुहोस्" + "गृह स्क्रिनमा" "प्रणालीको पूर्वनिर्धारित सेटिङ प्रयोग गर्नुहोस्" "वर्ग" "वर्गाकार वृत्त" @@ -119,9 +123,6 @@ "%1$s: मार्फत फोल्डर सिर्जना गर्नुहोस्" "फोल्डर सिर्जना गरियो" "गृह स्क्रिनमा सार्नुहोस्" - "स्क्रिनलाई बायाँ सार्नुहोस्" - "स्क्रिनलाई दायाँ सार्नुहोस्" - "स्क्रिन सारियो" "पुनःआकार मिलाउनुहोस्" "चौडाइ बढाउनुहोस्" "उँचाइ बढाउनुहोस्" @@ -137,8 +138,11 @@ "कार्यसम्बन्धी" "कार्य प्रोफाइल" "कार्यसम्बन्धी अनुप्रयोगहरू यहाँ प्राप्त गर्नुहोस्" - - + "कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा ब्याज छ र तपाईंको संगठनले यसलाई सुरक्षित राखेको छ । अझ सजिलो गरी पहुँच राख्नका लागि अनुप्रयोगहरूलाई आफ्नो गृहस्क्रिनमा सार्नुहोस्‌।" "तपाईंको सङ्गठनले व्यवस्थापन गरेको" "सूचना र अनुप्रयोगहरू निष्क्रिय छन्‌" + + + + diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index a686838f55..b926a8d747 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -40,9 +40,15 @@ "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" "Zoeken naar meer apps" "Meldingen" + + + + "Er is geen ruimte meer op dit startscherm." "Geen ruimte meer in het vak \'Favorieten\'" "Lijst met apps" + "Lijst met persoonlijke apps" + "Lijst met werk-apps" "Homepage" "Verwijderen" "Deïnstalleren" @@ -77,19 +83,17 @@ "Achtergrond" "Instellingen voor homepage" "Uitgeschakeld door je beheerder" - "Overzicht" - "Draaien van startscherm toestaan" - "Wanneer de telefoon gedraaid is" - "Huidige scherminstelling staat draaien niet toe" "Meldingsstipjes" "Aan" "Uit" "Toegang tot meldingen vereist" "Als je meldingsstipjes wilt weergeven, schakel je app-meldingen in voor %1$s" "Instellingen wijzigen" + "Meldingsstipjes weergeven" "Pictogram toevoegen aan startscherm" "Voor nieuwe apps" "Vorm van pictogram wijzigen" + "op het startscherm" "Systeemstandaard gebruiken" "Vierkant" "Squircle" @@ -119,9 +123,6 @@ "Map maken met: %1$s" "Map gemaakt" "Verplaatsen naar startscherm" - "Scherm naar links verplaatsen" - "Scherm naar rechts verplaatsen" - "Scherm verplaatst" "Formaat aanpassen" "Breedte vergroten" "Hoogte vergroten" @@ -137,8 +138,9 @@ "Werk" "Werkprofiel" "Zoek hier naar werk-apps" - - + "Elke werk-app heeft een badge en wordt beveiligd door je organisatie. Verplaats apps naar je startscherm voor snelle toegang." "Beheerd door je organisatie" "Meldingen en apps zijn uitgeschakeld" + "Sluiten" + "Gesloten" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index f81c4a2e7a..26de62ac6b 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ" "ਹੋਰ ਐਪਾਂ ਖੋਜੋ" "ਸੂਚਨਾਵਾਂ" + + + + "ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।" "ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।" "ਐਪ ਸੂਚੀ" + "ਨਿੱਜੀ ਐਪਾਂ ਦੀ ਸੂਚੀ" + "ਕਾਰਜ-ਸਥਾਨ ਸੰਬੰਧੀ ਐਪਾਂ ਦੀ ਸੂਚੀ" "ਹੋਮ" "ਹਟਾਓ" "ਅਣਸਥਾਪਤ ਕਰੋ" @@ -77,19 +83,17 @@ "ਵਾਲਪੇਪਰ" "ਹੋਮ ਸੈਟਿੰਗਾਂ" "ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ" - "ਰੂਪ-ਰੇਖਾ" - "ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ" - "ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ" - "ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ" "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" "ਸੂਚਨਾ ਪਹੁੰਚ ਲੋੜੀਂਦੀ ਹੈ" "ਸੂਚਨਾ ਬਿੰਦੂਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" + "ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ" "ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" "ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ" "ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ" "ਵਰਗ" "ਵਰਗਾਕਾਰ-ਚੱਕਰ" @@ -119,9 +123,6 @@ "ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: %1$s" "ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ" "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ" - "ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਮੂਵ ਕਰੋ" - "ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਮੂਵ ਕਰੋ" - "ਸਕ੍ਰੀਨ ਨੂੰ ਮੂਵ ਕੀਤਾ ਗਿਆ" "ਮੁੜ ਆਕਾਰ ਦਿਓ" "ਚੌੜਾਈ ਵਧਾਓ" "ਉਂਚਾਈ ਵਧਾਓ" @@ -137,8 +138,11 @@ "ਕਾਰਜ-ਸਥਾਨ" "ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ" "ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ ਇੱਥੇ ਲੱਭੋ" - - + "ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।" "ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ" "ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ" + + + + diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 77aeee267a..e9b4cc93d8 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -43,6 +43,8 @@ "Brak miejsca na tym ekranie głównym." "Brak miejsca w Ulubionych" "Lista aplikacji" + "Lista aplikacji osobistych" + "Lista aplikacji do pracy" "Ekran główny" "Usuń" "Odinstaluj" @@ -79,19 +81,17 @@ "Tapety" "Ustawienia strony głównej" "Funkcja wyłączona przez administratora" - "Przegląd" - "Zezwalaj na obrót ekranu głównego" - "Po obróceniu telefonu" - "Obecne ustawienia wyświetlania nie pozwalają na obrót ekranu" "Plakietki z powiadomieniami" "Włączono" "Wyłączono" "Wymagany jest dostęp do powiadomień" "Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji %1$s" "Zmień ustawienia" + "Pokaż plakietki z powiadomieniami" "Dodaj ikonę do ekranu głównego" "W przypadku nowych aplikacji" "Zmień kształt ikon" + "na ekranie głównym" "Użyj ustawienia domyślnego" "Kwadrat" "Zaokrąglony kwadrat" @@ -121,9 +121,6 @@ "Utwórz folder z: %1$s" "Folder został utworzony" "Przenieś na ekran główny" - "Przenieś ekran w lewo" - "Przenieś ekran w prawo" - "Ekran został przeniesiony" "Zmień rozmiar" "Zwiększ szerokość" "Zwiększ wysokość" @@ -139,8 +136,7 @@ "Praca" "Profil służbowy" "Aplikacje do pracy" - - + "Każda aplikacja do pracy ma plakietkę, a o jej bezpieczeństwo dba Twoja organizacja. Aplikacje można przenieść na ekran główny, by były łatwiej dostępne." "Profil zarządzany przez Twoją organizację" "Powiadomienia i aplikacje są wyłączone" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index cd473fafa1..9517baf035 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -40,9 +40,15 @@ "Nenhuma aplicação correspondente a \"%1$s\"" "Pesquisar mais aplicações" "Notificações" + + + + "Sem espaço suficiente neste Ecrã principal." "Não existe mais espaço no tabuleiro de Favoritos" "Lista de aplicações" + "Lista de aplicações pessoais" + "Lista de aplicações de trabalho" "Ecrã principal" "Remover" "Desinstalar" @@ -77,19 +83,17 @@ "Imagens de fundo" "Definições da página inicial" "Desativada pelo gestor" - "Vista geral" - "Permitir rotação do ecrã principal" - "Quando o telemóvel é rodado" - "A definição de visualização atual não permite a rotação" "Pontos de notificação" "Ativada" "Desativada" "Acesso a notificações necessário" "Para mostrar os Pontos de notificação, ative as notificações de aplicações para o %1$s" "Alterar definições" + "Mostrar pontos de notificação" "Adicionar ícone ao ecrã principal" "Para novas aplicações" "Alterar forma do ícone" + "no ecrã principal" "Utilizar a predefinição do sistema" "Quadrado" "Quadrado e círculo" @@ -119,9 +123,6 @@ "Criar pasta com: %1$s" "Pasta criada" "Mover para o Ecrã principal" - "Mover ecrã para a esquerda" - "Mover ecrã para a direita" - "Ecrã movido" "Redimensionar" "Aumentar largura" "Aumentar altura" @@ -137,8 +138,9 @@ "Trabalho" "Perfil de trabalho" "Encontrar as aplicações de trabalho aqui" - - + "Cada aplicação de trabalho apresenta um emblema, pelo que a sua entidade a mantém em segurança. Pode mover as aplicações para o ecrã principal para facilitar o acesso." "Gerido pela sua entidade" "As notificações e as aplicações estão desativadas." + "Fechar" + "Fechado" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 495ba4c2f9..88118d9fe7 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -40,9 +40,15 @@ "Nu s-a găsit nicio aplicație pentru „%1$s\"" "Căutați mai multe aplicații" "Notificări" + + + + "Nu mai este loc pe acest Ecran de pornire." "Spațiu epuizat în bara Preferate" "Lista de aplicații" + "Lista de aplicații personale" + "Lista de aplicații de serviciu" "Ecran de pornire" "Eliminați" "Dezinstalați" @@ -78,19 +84,17 @@ "Imagini de fundal" "Setări pentru ecranul de pornire" "Dezactivată de administrator" - "Prezentare generală" - "Permiteți rotirea ecranului de pornire" - "Când telefonul este rotit" - "Setarea actuală a afișajului nu permite rotirea" "Puncte de notificare" "Activat" "Dezactivat" "Este necesar accesul la notificări" "Pentru a afișa punctele de notificare, activați notificările din aplicație pentru %1$s" "Modificați setările" + "Afișați punctele de notificare" "Adaugă pictograme în ecranul de pornire" "Pentru aplicații noi" "Schimbați forma pictogramei" + "pe ecranul de pornire" "Folosiți setarea prestabilită a sistemului" "Pătrat" "Pătrat cu colțuri rotunjite" @@ -120,9 +124,6 @@ "Creați dosar cu: %1$s" "Dosar creat" "Mutați pe ecranul de pornire" - "Mutați ecranul la stânga" - "Mutați ecranul la dreapta" - "Ecran mutat" "Redimensionați" "Creșteți lățimea" "Creșteți înălțimea" @@ -138,8 +139,9 @@ "Profesionale" "Profil de serviciu" "Găsiți aplicații de serviciu aici" - - + "Fiecare aplicație de serviciu are o insignă și este păstrată în siguranță de organizația dvs. Mutați aplicațiile pe ecranul de pornire pentru acces mai ușor." "Gestionat de organizația dvs." "Notificările și aplicațiile sunt dezactivate" + "Închideți" + "Închis" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 5024fecf8b..f1ffba4a4b 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" "තව යෙදුම් සඳහා සොයන්න" "දැනුම්දීම්" + + + + "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." "ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත" "යෙදුම් ලැයිස්තුව" + "පෞද්ගලික යෙදුම් ලැයිස්තුව" + "වැඩ යෙදුම් ලැයිස්තුව" "මුල් පිටුව" "ඉවත් කරන්න" "අස්ථාපනය කරන්න" @@ -77,19 +83,17 @@ "වෝල්පේපර" "Home සැකසීම්" "ඔබගේ පරිපාලක විසින් අබල කරන ලදී" - "දළ විශ්ලේෂණය" - "මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න" - "දුරකථනය කරකවන විට" - "වත්මන් සංදර්ශක සැකසීම් කරකැවීමට සහාය නොදක්වයි" "දැනුම්දීම් තිත්" "ක්‍රියාත්මකයි" "ක්‍රියාවිරහිතයි" "දැනුම්දීම් ප්‍රවේශය අවශ්‍යයි" "දැනුම්දීම් තිත් පෙන්වීමට, %1$s සඳහා යෙදුම් දැනුම්දීම් සබල කරන්න" "සැකසීම් වෙනස් කරන්න" + "දැනුම් දීමේ තිත් පෙන්වන්න" "මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න" "නව යෙදුම් සඳහා" "නිරූපක හැඩය වෙනස් කරන්න" + "මුල් පිටු තිරය මත" "පද්ධති පෙරනිමි භාවිත කරන්න" "සමචතුරස්‍රය" "හතරැස් කවය" @@ -119,9 +123,6 @@ "මේ සමග ෆෝල්ඩරය සාදන්න: %1$s" "ෆෝල්ඩරය සාදන ලදි" "මුල් තිරය වෙත ගෙන යන්න" - "තිරය වම් පැත්තට ගෙනයන්න" - "තිරය දකුණු පැත්තට ගෙනයන්න" - "තිරය ගෙන යන ලදි" "නැවත ප්‍රමාණගත කිරීම" "පළල වැඩි කරන්න" "උස වැඩි කරන්න" @@ -137,8 +138,9 @@ "කාර්යාලය" "කාර්යාල පැතිකඩ" "මෙහි කාර්යාල යෙදුම් සොයා ගන්න" - - + "සෑම කාර්යාල යෙදුමකම ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය මගින් සුරක්ෂිතව තබා ගනී. වඩාත් පහසු ප්‍රවේශයකට යෙදුම් ඔබේ මුල් පිටු තිරය වෙත ගෙන යන්න." "ඔබේ සංවිධානය විසින් කළමනාකරණය කරනු ලැබේ" "දැනුම්දීම් සහ යෙදුම් ක්‍රියාවිරහිතයි" + "වසන්න" + "වසා ඇත" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 58baac4851..546787f9a5 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -40,9 +40,15 @@ "Nenašli sa žiadne aplikácie zodpovedajúce dopytu %1$s" "Hľadať ďalšie aplikácie" "Upozornenia" + + + + "Na tejto ploche už nie je miesto" "Na paneli Obľúbené položky už nie je miesto" "Zoznam aplikácií" + "Zoznam osobných aplikácií" + "Zoznam pracovných aplikácií" "Domovská stránka" "Odstrániť" "Odinštalovať" @@ -79,19 +85,17 @@ "Tapety" "Nastavenia služby Home" "Zakázané vaším správcom" - "Prehľad" - "Povoliť otáčanie plochy" - "Pri otočení telefónu" - "Aktuálne nastavenie obrazovky nepovoľuje otáčanie" "Bodky upozornení" "Zapnuté" "Vypnuté" "Vyžaduje sa prístup k upozorneniam" "Ak chcete, aby sa zobrazovali bodky upozornení, zapnite upozornenia aplikácie %1$s" "Zmeniť nastavenia" + "Zobrazovať bodky upozornení" "Pridať ikonu na plochu" "Pri inštalácii novej aplikácie" "Zmeniť tvar ikony" + "na ploche" "Použiť predvolené nastavenie systému" "Štvorec" "Okrúhly štvorec" @@ -121,9 +125,6 @@ "Vytvoriť priečinok pomocou: %1$s" "Priečinok bol vytvorený" "Presunúť na plochu" - "Presunúť obrazovku doľava" - "Presunúť obrazovku doprava" - "Obrazovka bola posunutá" "Zmeniť veľkosť" "Zvýšiť šírku" "Zväčšiť výšku" @@ -139,8 +140,9 @@ "Pracovné" "Pracovný profil" "Tu nájdete pracovné aplikácie" - - + "Všetky pracovné aplikácie majú štítok a sú bezpečne uchovávané vašou organizáciou. Ak chcete mať k aplikáciám ľahší prístup, presuňte ich na plochu." "Spravované vašou organizáciou" "Upozornenia a aplikácie sú vypnuté" + "Zavrieť" + "Zavreté" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 758c5b2185..488295e9d4 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -40,9 +40,15 @@ "Ni aplikacij, ki bi ustrezale poizvedbi »%1$s«" "Iskanje več aplikacij" "Obvestila" + + + + "Na tem začetnem zaslonu ni več prostora." "V vrstici za priljubljene ni več prostora" "Seznam aplikacij" + "Seznam osebnih aplikacij" + "Seznam delovnih aplikacij" "Začetni zaslon" "Odstrani" "Odstrani" @@ -79,19 +85,17 @@ "Ozadja" "Nastavitve začetnega zaslona" "Onemogočil skrbnik." - "Pregled" - "Omogočanje sukanja začetnega zaslona" - "Ko se telefon zasuka" - "Trenutna nastavitev zaslona ne dovoljuje sukanja" "Obvestilne pike" "Vklopljeno" "Izklopljeno" "Potreben je dostop do obvestil" "Za prikaz obvestilnih pik vklopite obvestila aplikacije %1$s" "Spremeni nastavitve" + "Pokaži obvestilne pike" "Dodaj ikono na začetni zaslon" "Za nove aplikacije" "Spremeni obliko ikon" + "na začetnem zaslonu" "Uporabi privzeto nastavitev sistema" "Kvadrat" "Zaobljen kvadrat" @@ -121,9 +125,6 @@ "Ustvarjanje mape s tem: %1$s" "Mapa je ustvarjena" "Premik na začetni zaslon" - "Premik zaslona levo" - "Premika zaslona desno" - "Zaslon je bil premaknjen" "Spreminjanje velikosti" "Povečanje širine" "Povečanje višine" @@ -139,8 +140,9 @@ "Služba" "Delovni profil" "Tukaj poiščite delovne aplikacije" - - + "Vsaka delovna aplikacija ima značko. Za varnost teh aplikacij skrbi vaša organizacija. Za preprostejši dostop premaknite aplikacije na začetni zaslon." "Upravlja vaša organizacija" "Obvestila in aplikacije – izklopljeno" + "Zapri" + "Zaprto" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index d260a4b774..8d58d1c2de 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -40,9 +40,15 @@ "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" "Kërko për më shumë aplikacione" "Njoftimet" + + + + "Nuk ka më hapësirë në këtë ekran bazë." "Nuk ka më hapësirë në tabakanë \"Të preferuarat\"" "Lista e aplikacioneve" + "Lista e aplikacioneve personale" + "Lista e aplikacioneve të punës" "Faqja kryesore" "Hiqe" "Çinstalo" @@ -77,19 +83,17 @@ "Imazhet e sfondit" "Cilësimet e Home" "Çaktivizuar nga administratori" - "Përmbledhje" - "Lejo rrotullimin e ekranit kryesor" - "Kur telefoni rrotullohet" - "Cilësimi aktuali i afishimit nuk lejon rrotullimin" "Pikat e njoftimeve" "Aktiv" "Joaktiv" "Nevojitet qasja në njoftime" "Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për %1$s" "Ndrysho cilësimet" + "Shfaq pikat e njoftimeve" "Shto ikonë në ekranin bazë" "Për aplikacionet e reja" "Ndrysho formën e ikonës" + "në ekranin bazë" "Përdor parazgjedhjen e sisteit" "Katror" "Katror me kënde të rrumbullakëta" @@ -119,9 +123,6 @@ "Krijo një dosje me: %1$s" "Dosja u krijua" "Zhvendose në Ekranin bazë" - "Zhvendose ekranin në të majtë" - "Zhvendose ekranin në të djathtë" - "Ekrani u zhvendos" "Ndrysho madhësinë" "Rrit gjerësinë" "Rrit lartësinë" @@ -137,8 +138,9 @@ "Punë" "Profili i punës" "Gjej këtu aplikacionet e punës" - - + "Secili aplikacion pune ka një distinktiv dhe mbahet i sigurt nga organizata jote. Zhvendosi aplikacionet e punës në ekranin tënd kryesor për qasje më të lehtë." "Menaxhohet nga organizata jote" "Njoftimet dhe aplikacionet janë joaktive" + "Mbyll" + "Mbyllur" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 85e814b813..e58e1987a7 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -40,9 +40,15 @@ "Inga appar som matchar %1$s hittades" "Sök efter fler appar" "Aviseringar" + + + + "Det finns inte plats för mer på den här startskärmen." "Favoritfältet är fullt" "Applista" + "Listan Personliga appar" + "Listan Jobbappar" "Startskärm" "Ta bort" "Avinstallera" @@ -77,19 +83,17 @@ "Bakgrunder" "Inställningar för startsidan" "Inaktiverat av administratören" - "Översikt" - "Tillåt rotering av startskärmen" - "När mobilen vrids" - "Rotering tillåts inte i de nuvarande skärminställningarna" "Aviseringsprickar" "På" "Av" "Åtkomst till aviseringar krävs" "Aktivera appaviseringar för %1$s om du vill att aviseringsprickar ska visas" "Ändra inställningar" + "Visa aviseringsprickar" "Lägg till ikonen på startskärmen" "För nya appar" "Ändra form på ikoner" + "på startskärmen" "Använd systemstandard" "Kvadrat" "Kvirkel" @@ -119,9 +123,6 @@ "Skapa mapp med: %1$s" "Mappen har skapats" "Flytta till startskärmen" - "Flytta skärmen till vänster" - "Flytta skärmen till höger" - "Skärmen har flyttats" "Ändra storlek" "Öka bredden" "Öka höjden" @@ -137,8 +138,9 @@ "Arbete" "Jobbprofil" "Här hittar du jobbappar" - - + "Alla jobbappar har ett märke och organisationen ser till att de är skyddade. Flytta apparna till startskärmen så kommer du åt dem lättare." "Hanteras av organisationen" "Aviseringar och appar är inaktiverade" + "Stäng" + "Stängd" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index a9dc8a6e84..80666e97c2 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -40,9 +40,15 @@ "Haikupata programu zozote zinazolingana na \"%1$s\"" "Tafuta programu zaidi" "Arifa" + + + + "Hakuna nafasi katika skrini hii ya Mwanzo." "Hakuna nafasi zaidi katika treya ya Vipendeleo" "Orodha ya programu" + "Orodha ya programu za binafsi" + "Orodha ya programu za kazini" "Mwanzo" "Ondoa" "Ondoa" @@ -79,19 +85,17 @@ "Mandhari" "Mipangilio ya ukurasa wa mwanzo" "Imezimwa na msimamizi wako" - "Muhtasari" - "Ruhusu kuzungusha skrini ya Kwanza" - "Simu inapozungushwa" - "Mipangilio ya sasa ya sehemu ya Onyesho hairuhusu kuzungusha" "Vitone vya arifa" "Imewashwa" "Imezimwa" "Inahitaji idhini ya kufikia arifa" "Ili kuonyesha Vitone vya Arifa, washa kipengele cha arifa za programu katika %1$s" "Badilisha mipangilio" + "Onyesha kitone cha arifa" "Ongeza aikoni kwenye Skrini ya kwanza" "Kwa ajili ya programu mpya" "Badilisha umbo la aikoni" + "kwenye Skrini ya mwanzo" "Tumia umbo chaguo-msingi la mfumo" "Mraba" "Mstatili wenye pembe duara" @@ -121,9 +125,6 @@ "Unda folda ukitumia: %1$s" "Folda imeundwa" "Hamishia Skrini ya kwanza" - "Sogeza skrini kushoto" - "Sogeza skrini kulia" - "Skrini imesogezwa" "Badilisha ukubwa" "Ongeza upana" "Ongeza urefu" @@ -139,8 +140,9 @@ "Kazini" "Wasifu wa kazini" "Pata programu za kazi hapa" - - + "Kila programu ya kazi ina beji na hulindwa na shirika lako. Hamishia programu kwenye skrini yako ya kwanza ili uzifikie kwa urahisi." "Inasimamiwa na shirika lako" "Vipenge vya arifa na programu vimezimwa" + "Funga" + "Imefungwa" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 25bfc02f44..8a7d66622e 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" "கூடுதல் பயன்பாடுகளைத் தேடு" "அறிவிப்புகள்" + + + + "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" "பயன்பாடுகளின் பட்டியல்" + "தனிப்பட்ட ஆப்ஸ் பட்டியல்" + "பணி ஆப்ஸ் பட்டியல்" "முகப்பு" "அகற்று" "நிறுவல் நீக்கு" @@ -77,19 +83,17 @@ "வால்பேப்பர்கள்" "முகப்பு அமைப்புகள்" "உங்கள் நிர்வாகி முடக்கியுள்ளார்" - "மேலோட்டப் பார்வை" - "முகப்புத் திரை சுழற்சியை அனுமதி" - "மொபைலைச் சுழற்றும் போது" - "தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை" "அறிவிப்புப் புள்ளிகள்" "ஆன்" "முடக்கப்பட்டுள்ளது" "அறிவிப்பிற்கான அணுகல் தேவை" "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" "அமைப்புகளை மாற்று" + "அறிவிப்புப் புள்ளிகளைக் காட்டு" "முகப்புத் திரையில் ஐகானைச் சேர்" "புதிய பயன்பாடுகளுக்கு" "ஐகான் வடிவத்தை மாற்று" + "முகப்புத் திரையில்" "அமைப்பின் இயல்புநிலையைப் பயன்படுத்து" "சதுரம்" "சதுரவட்டம்" @@ -119,9 +123,6 @@ "இதனுடன் கோப்புறையை உருவாக்கும்: %1$s" "கோப்புறை உருவாக்கப்பட்டது" "முகப்புத் திரைக்கு நகர்த்து" - "திரையை இடப்புறம் நகர்த்து" - "திரையை வலப்புறம் நகர்த்து" - "திரை நகர்த்தப்பட்டது" "அளவு மாற்று" "அகலத்தை அதிகரி" "உயரத்தை அதிகரி" @@ -137,8 +138,11 @@ "பணி" "பணி விவரம்" "பணி ஆப்ஸை இங்கு காணலாம்" - - + "ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்." "உங்கள் நிறுவனம் நிர்வகிக்கிறது" "ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன" + + + + diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 6af501138f..b69afdc4e6 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు" "మరిన్ని యాప్‌ల కోసం వెతుకు" "నోటిఫికేషన్‌లు" + + + + "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" "అనువర్తనాల జాబితా" + "వ్యక్తిగత యాప్‌ల జాబితా" + "కార్యాలయ యాప్‌ల జాబితా" "హోమ్" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" @@ -77,19 +83,17 @@ "వాల్‌పేపర్‌లు" "హోమ్ సెట్టింగ్‌లు" "మీ నిర్వాహకులు నిలిపివేసారు" - "స్థూలదృష్టి" - "హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి" - "ఫోన్‌‌ను తిప్పినప్పుడు" - "ప్రస్తుత డిస్‌ప్లే సెట్టింగ్ భ్రమణాన్ని అనుమతించలేదు" "నోటిఫికేషన్ డాట్‌లు" "ఆన్" "ఆఫ్" "నోటిఫికేషన్ యాక్సెస్ అవసరం" "నోటిఫికేషన్ డాట్‌లను చూపించడానికి %1$sకు యాప్ నోటిఫికేషన్‌లను ఆన్ చేయండి" "సెట్టింగ్‌లను మార్చు" + "నోటిఫికేషన్ డాట్‌లను చూపుతుంది" "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" "కొత్త యాప్‌ల కోసం" "చిహ్న ఆకారాన్ని మార్చు" + "హోమ్ స్క్రీన్‌పై" "సిస్టమ్ డిఫాల్ట్‌ను ఉపయోగించండి" "చతురస్రం" "చతురస్రాకార వృత్తం" @@ -119,9 +123,6 @@ "ఈ పేరుతో ఫోల్డర్‌ను సృష్టించండి: %1$s" "ఫోల్డర్ సృష్టించబడింది" "హోమ్‌స్క్రీన్‌కు తరలించు" - "స్క్రీన్‌ను ఎడమవైపుకి జరుపు" - "స్క్రీన్‌ను కుడివైపుకి జరుపు" - "స్క్రీన్ జరపబడింది" "పరిమాణం మార్చు" "వెడల్పును పెంచు" "ఎత్తును పెంచు" @@ -137,8 +138,11 @@ "కార్యాలయం" "కార్యాలయ ప్రొఫైల్" "కార్యాలయ యాప్‌లను ఇక్కడ కనుగొనండి" - - + "ప్రతి కార్యాలయ యాప్‌కు బ్యాడ్జ్‌ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్‌లను మీ హోమ్ స్క్రీన్‌కి తరలించండి." "మీ సంస్థ ద్వారా నిర్వహించబడతాయి" "నోటిఫికేషన్‌లు మరియు యాప్‌లు ఆఫ్ చేయబడ్డాయి" + + + + diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 866bfc913c..c032305af8 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -40,9 +40,15 @@ "ไม่พบแอปที่ตรงกับ \"%1$s\"" "ค้นหาแอปเพิ่มเติม" "การแจ้งเตือน" + + + + "ไม่มีที่ว่างในหน้าจอหลักนี้" "ไม่มีพื้นที่เหลือในถาดรายการโปรด" "รายชื่อแอป" + "รายการแอปส่วนตัว" + "รายการแอปสำหรับทำงาน" "หน้าแรก" "นำออก" "ถอนการติดตั้ง" @@ -77,19 +83,17 @@ "วอลเปเปอร์" "การตั้งค่าหน้าแรก" "ปิดใช้โดยผู้ดูแลระบบ" - "ภาพรวม" - "อนุญาตให้หมุนหน้าจอหลัก" - "เมื่อหมุนโทรศัพท์" - "การตั้งค่าการแสดงผลปัจจุบันไม่อนุญาตให้มีการหมุน" "จุดแจ้งเตือน" "เปิด" "ปิด" "ต้องได้รับสิทธิ์เข้าถึงการแจ้งเตือน" "เปิดการแจ้งเตือนแอปของ %1$s เพื่อแสดงจุดแจ้งเตือน" "เปลี่ยนการตั้งค่า" + "แสดงจุดแจ้งเตือน" "เพิ่มไอคอนในหน้าจอหลัก" "สำหรับแอปใหม่" "เปลี่ยนรูปร่างไอคอน" + "ในหน้าจอหลัก" "ใช้ค่าเริ่มต้นของระบบ" "สี่เหลี่ยมจัตุรัส" "สี่เหลี่ยมขอบมน" @@ -119,9 +123,6 @@ "สร้างโฟลเดอร์ด้วย: %1$s" "สร้างโฟลเดอร์แล้ว" "ย้ายไปที่หน้าจอหลัก" - "เลื่อนหน้าจอไปทางซ้าย" - "เลื่อนหน้าจอไปทางขวา" - "ย้ายหน้าจอแล้ว" "ปรับขนาด" "เพิ่มความกว้าง" "เพิ่มความสูง" @@ -137,8 +138,9 @@ "งาน" "โปรไฟล์งาน" "หาแอปงานที่นี่" - - + "แอปงานแต่ละแอปมีป้ายและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น" "จัดการโดยองค์กร" "ปิดการแจ้งเตือนและแอปอยู่" + "ปิด" + "ปิด" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 1f75b6781e..22e6bda95f 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" ile eşleşen uygulama bulunamadı" "Başka uygulamalar ara" "Bildirimler" + + + + "Bu Ana ekranda yer kalmadı." "Favoriler tepsisinde başka yer kalmadı" "Uygulamalar listesi" + "Kişisel uygulamalar listesi" + "İş uygulamaları listesi" "Ana ekran" "Kaldır" "Yüklemeyi kaldır" @@ -77,19 +83,17 @@ "Duvar Kağıtları" "Ana ekran ayarları" "Yöneticiniz tarafından devre dışı bırakıldı" - "Genel bakış" - "Ana ekranı döndürmeye izin ver" - "Telefon döndürüldüğünde" - "Mevcut Ekran ayarı, döndürmeye izin vermiyor" "Bildirim noktaları" "Açık" "Kapalı" "Bildirim erişimi gerekli" "Bildirim Noktaları\'nı göstermek için %1$s uygulamasının bildirimlerini açın" "Ayarları değiştir" + "Bildirim noktalarını göster" "Ana ekrana simge ekle" "Yeni uygulamalar için" "Simge şeklini değiştir" + "Ana ekranda" "Sistem varsayılanını kullan" "Kare" "Kare-daire" @@ -119,9 +123,6 @@ "Şu öğeyle klasör oluştur: %1$s" "Klasör oluşturuldu" "Ana ekrana taşı" - "Ekranı sola taşı" - "Ekranı sağa taşı" - "Ekran taşındı" "Yeniden boyutlandır" "Genişliği artır" "Yüksekliği artır" @@ -137,8 +138,9 @@ "İş" "İş profili" "İş uygulamalarını burada bulabilirsiniz" - - + "Her iş uygulamasında, uygulama güvenliğinin kuruluşunuz tarafından sağlandığını gösteren bir rozet bulunur. Daha kolay erişim için uygulamaları Ana ekranınıza taşıyın." "Kuruluşunuz tarafından yönetiliyor" "Bildirimler ve uygulamalar kapalı" + "Kapat" + "Kapalı" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index ea8a4d7e70..0344cc0d2f 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -40,9 +40,15 @@ "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" "مزید ایپس تلاش کریں" "اطلاعات" + + + + "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" "پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے" "ایپس کی فہرست" + "ذاتی ایپس کی فہرست" + "دفتری ایپس کی فہرست" "ہوم" "ہٹائیں" "اَن انسٹال کریں" @@ -77,19 +83,17 @@ "وال پیپرز" "ہوم ترتیبات" "آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا" - "مجموعی جائزہ" - "ہوم اسکرین گھمانے کی اجازت دیں" - "جب فون گھمایا جاتا ہے" - "موجودہ ڈسپلے ترتیب گھمانے کی اجازت نہیں دیتی" "اطلاعاتی ڈاٹس" "آن" "آف" "اطلاعاتی رسائی درکار ہے" "اطلاعاتی ڈاٹس دکھانے کی خاطر %1$s کیلئے ایپ کی اطلاعات آن کریں" "ترتیبات تبدیل کریں" + "اطلاعاتی ڈاٹس دکھائیں" "آئیکن کو ہوم اسکرین میں شامل کریں" "نئی ایپس کیلئے" "آئیکن کی شکل تبدیل کریں" + "ہوم اسکرین پر" "سسٹم ڈیفالٹ کا استعمال کریں" "مربع" "اسکورکل" @@ -119,9 +123,6 @@ "اس کے ساتھ فولڈر بنائیں: %1$s" "فولڈر بنا دیا گیا" "ہوم اسکرین میں منتقل کریں" - "اسکرین کو بائیں منتقل کریں" - "اسکرین کو دائیں منتقل کریں" - "اسکرین منتقل کر دی گئی" "سائز تبدیل کریں" "چوڑائی بڑھائیں" "اونچائی بڑھائیں" @@ -137,8 +138,11 @@ "دفتری" "دفتری پروفائل" "یہاں دفتری ایپس تلاش کریں" - - + "ہر دفتری ایپ میں ایک بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔" "آپ کی تنظیم کے زیر انتظام" "اطلاعات اور ایپس آف ہیں" + + + + diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 70dc41fa73..ea4cf6d475 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -40,9 +40,15 @@ "“%1$s” bilan mos hech qanday ilova topilmadi" "Boshqa ilovalarni qidirish" "Bildirishnomalar" + + + + "Uy ekranida bitta ham xona yo‘q." "Ajratilganlarda birorta ham xona yo‘q" "Ilovalar ro‘yxati" + "Shaxsiy ilovalar ro‘yxati" + "Ishchi ilovalar ro‘yxati" "Bosh sahifa" "Olib tashlash" "O‘chirib tashlash" @@ -77,19 +83,17 @@ "Fon rasmlari" "Home sozlamalari" "Administrator tomonidan o‘chirilgan" - "Umumiy ko‘rinish" - "Asosiy ekranni aylantirishga ruxsat berish" - "Telefon burilganda" - "Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi" "Bildirishnoma belgilari" "Yoniq" "O‘chiq" "Bildirishnomalarga ruxsat berilmagan" "Bildirishnoma belgilarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" "Sozlamalarni o‘zgartirish" + "Bildirishnoma belgilarini ko‘rsatish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" "Ikonka shaklini o‘zgartirish" + "Bosh ekranda" "Standart tizim parametrlaridan foydalanish" "Kvadrat" "Qirralari aylana kvadrat" @@ -119,9 +123,6 @@ "%1$s bilan jild yaratish" "Jild yaratildi" "Bosh ekranga ko‘chirish" - "Ekranni chapga siljitish" - "Ekranni o‘ngga siljitish" - "Ekran siljitildi" "O‘lchamini o‘zgartirish" "Enini uzaytirish" "Bo‘yini uzaytirish" @@ -137,8 +138,9 @@ "Ishchi" "Ishchi profil" "Ishga oid ilovalarni shu yerdan topish mumkin" - - + "Nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring." "Tashkilotingiz tomonidan boshqariladi" "Bildirishnomalar va ilovalar faol emas" + "Yopish" + "Yopiq" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 1b8558f40d..27a9f80d01 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -40,9 +40,15 @@ "Không tìm thấy ứng dụng nào phù hợp với \"%1$s\"" "Tìm kiếm thêm ứng dụng" "Thông báo" + + + + "Không còn chỗ trên Màn hình chính này." "Không còn chỗ trong khay Mục yêu thích" "Danh sách ứng dụng" + "Danh sách ứng dụng cá nhân" + "Danh sách ứng dụng công việc" "Màn hình chính" "Xóa" "Gỡ cài đặt" @@ -77,19 +83,17 @@ "Hình nền" "Cài đặt trang chủ" "Bị tắt bởi quản trị viên của bạn" - "Tổng quan" - "Cho phép xoay Màn hình chính" - "Khi xoay điện thoại" - "Cài đặt Hiển thị hiện tại không cho phép xoay" "Dấu chấm thông báo" "Đang bật" "Đã tắt" "Cần quyền truy cập thông báo" "Để hiển thị Dấu chấm thông báo, hãy bật thông báo ứng dụng cho %1$s" "Thay đổi cài đặt" + "Hiển thị dấu chấm thông báo" "Thêm biểu tượng vào màn hình chính" "Cho ứng dụng mới" "Thay đổi hình dạng biểu tượng" + "trên Màn hình chính" "Sử dụng mặc định của hệ thống" "Hình vuông" "Hình vuông cạnh bo tròn" @@ -119,9 +123,6 @@ "Tạo thư mục bằng: %1$s" "Đã tạo thư mục" "Di chuyển đến màn hình chính" - "Di chuyển màn hình sang trái" - "Di chuyển màn hình sang phải" - "Đã di chuyển màn hình" "Đổi kích thước" "Tăng chiều rộng" "Tăng chiều cao" @@ -137,8 +138,9 @@ "Cơ quan" "Hồ sơ công việc" "Tìm ứng dụng công việc tại đây" - - + "Mỗi ứng dụng công việc đều có một huy hiệu và được tổ chức của bạn bảo mật. Bạn có thể di chuyển ứng dụng đến Màn hình chính để truy cập dễ dàng hơn." "Do tổ chức của bạn quản lý" "Thông báo và ứng dụng đang tắt" + "Đóng" + "Đã đóng" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 86c52465b1..85d644af54 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -40,9 +40,15 @@ "找不到與「%1$s」相符的應用程式" "搜尋更多應用程式" "通知" + + + + "主畫面已無空間。" "我的收藏寄存區沒有足夠空間" "應用程式清單" + "個人應用程式清單" + "工作應用程式清單" "主畫面" "移除" "解除安裝" @@ -77,19 +83,17 @@ "桌布" "Home 設定" "已由您的管理員停用" - "概覽" - "允許主畫面旋轉" - "當手機旋轉時" - "「目前顯示屏」設定不允許旋轉" "通知圓點" "開啟" "關閉" "需要獲取通知存取權" "如要顯示「通知圓點」,請開啟「%1$s」的應用程式通知功能" "變更設定" + "顯示通知圓點" "將圖示加到主畫面" "適用於新安裝的應用程式" "變更圖示形狀" + "在主畫面上" "使用系統預設設定" "正方形" "方圓形" @@ -119,9 +123,6 @@ "使用以下項目建立資料夾:%1$s" "已建立資料夾" "移動至主畫面" - "向左移動螢幕" - "向右移動螢幕" - "已移動螢幕" "重新調整大小" "增加闊度" "增加高度" @@ -137,8 +138,9 @@ "商務" "工作設定檔" "請在此處尋找工作應用程式" - - + "每個工作應用程式都有一個徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。" "由您的機構管理" "通知和應用程式已關閉" + "關閉" + "已關閉" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 1bac58a124..07917b649d 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -40,9 +40,15 @@ "找不到與「%1$s」相符的應用程式" "搜尋更多應用程式" "通知" + + + + "這個主螢幕已無空間。" "「我的最愛」匣已無可用空間" "應用程式清單" + "個人應用程式清單" + "辦公應用程式清單" "主螢幕" "移除" "解除安裝" @@ -77,19 +83,17 @@ "桌布" "Home 設定" "已由你的管理員停用" - "總覽" - "允許旋轉主螢幕" - "當手機旋轉時" - "目前的顯示設定不允許旋轉畫面" "通知圓點" "已啟用" "已停用" "需要取得通知存取權" "如要顯示通知圓點,請開啟「%1$s」的應用程式通知功能" "變更設定" + "顯示通知圓點" "將圖示加到主螢幕" "適用於新安裝的應用程式" "變更圖示形狀" + "在主螢幕上" "使用系統預設值" "正方形" "方圓形" @@ -119,9 +123,6 @@ "建立「%1$s」資料夾" "已建立資料夾" "移至主畫面" - "將畫面往左移" - "將畫面往右移" - "已移動畫面" "調整大小" "增加寬度" "增加高度" @@ -137,8 +138,9 @@ "公司" "Work 設定檔" "在這裡尋找辦公應用程式" - - + "每個辦公應用程式都有徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。" "由貴機構所管理" "已關閉通知和應用程式" + "關閉" + "已關閉" diff --git a/res/values/config.xml b/res/values/config.xml index 3f727cf38e..a40afe1138 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -119,6 +119,10 @@ + + + + 150 80 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index f8f9c2ae99..3a06cdd815 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -37,6 +37,7 @@ 2dp 80dp 0dp + 24dp 8dp @@ -95,7 +96,12 @@ 8dp - + + 56dp + + 106dp + + 8dp 16dp diff --git a/res/values/strings.xml b/res/values/strings.xml index b39cb04057..7d5d81c60e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -72,6 +72,11 @@ Notifications + + Touch & hold to pick up a shortcut. + + Double-tap & hold to pick up a shortcut or use custom actions. + No more room on this Home screen. @@ -196,6 +201,8 @@ Change icon shape + + on Home screen Use system default @@ -328,5 +335,6 @@ Managed by your organization Notifications and apps are off - + Close + Closed diff --git a/res/values/styles.xml b/res/values/styles.xml index ac6a6b1b53..38b5daedbf 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -112,9 +112,9 @@ true center_horizontal true - marquee ?android:attr/textColorSecondary sans-serif-condensed + false 0 diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index fc5ce8f2b7..f34cf0dc50 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -24,9 +24,9 @@ import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -92,7 +92,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public final void close(boolean animate) { animate &= !Utilities.isPowerSaverOn(getContext()); handleClose(animate); - Launcher.getLauncher(getContext()).getUserEventDispatcher() + BaseActivity.fromContext(getContext()).getUserEventDispatcher() .resetElapsedContainerMillis("container closed"); } @@ -120,8 +120,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } protected static T getOpenView( - Launcher launcher, @FloatingViewType int type) { - DragLayer dragLayer = launcher.getDragLayer(); + BaseDraggingActivity activity, @FloatingViewType int type) { + BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { @@ -136,16 +136,17 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch return null; } - public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) { - AbstractFloatingView view = getOpenView(launcher, type); + public static void closeOpenContainer(BaseDraggingActivity activity, + @FloatingViewType int type) { + AbstractFloatingView view = getOpenView(activity, type); if (view != null) { view.close(true); } } - public static void closeOpenViews(Launcher launcher, boolean animate, + public static void closeOpenViews(BaseDraggingActivity activity, boolean animate, @FloatingViewType int type) { - DragLayer dragLayer = launcher.getDragLayer(); + BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { @@ -159,16 +160,16 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } } - public static void closeAllOpenViews(Launcher launcher, boolean animate) { - closeOpenViews(launcher, animate, TYPE_ALL); - launcher.finishAutoCancelActionMode(); + public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) { + closeOpenViews(activity, animate, TYPE_ALL); + activity.finishAutoCancelActionMode(); } - public static void closeAllOpenViews(Launcher launcher) { - closeAllOpenViews(launcher, true); + public static void closeAllOpenViews(BaseDraggingActivity activity) { + closeAllOpenViews(activity, true); } - public static AbstractFloatingView getTopOpenView(Launcher launcher) { - return getOpenView(launcher, TYPE_ALL); + public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) { + return getOpenView(activity, TYPE_ALL); } } diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 77a430bb33..02d70c42ca 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -98,9 +98,12 @@ public abstract class BaseActivity extends Activity { mDPChangeListeners.add(listener); } + public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) { + mDPChangeListeners.remove(listener); + } + protected void dispatchDeviceProfileChanged() { - int count = mDPChangeListeners.size(); - for (int i = 0; i < count; i++) { + for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) { mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile); } } diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java new file mode 100644 index 0000000000..458f7b280f --- /dev/null +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.app.ActivityOptions; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Process; +import android.os.StrictMode; +import android.os.UserHandle; +import android.util.Log; +import android.view.ActionMode; +import android.view.View; +import android.widget.Toast; + +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.views.BaseDragLayer; + +/** + * Extension of BaseActivity allowing support for drag-n-drop + */ +public abstract class BaseDraggingActivity extends BaseActivity { + + private static final String TAG = "BaseDraggingActivity"; + + // The Intent extra that defines whether to ignore the launch animation + private static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = + "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; + + // When starting an action mode, setting this tag will cause the action mode to be cancelled + // automatically when user interacts with the launcher. + public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); + + private ActionMode mCurrentActionMode; + protected boolean mIsSafeModeEnabled; + + private OnStartCallback mOnStartCallback; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mIsSafeModeEnabled = getPackageManager().isSafeMode(); + } + + @Override + public void onActionModeStarted(ActionMode mode) { + super.onActionModeStarted(mode); + mCurrentActionMode = mode; + } + + @Override + public void onActionModeFinished(ActionMode mode) { + super.onActionModeFinished(mode); + mCurrentActionMode = null; + } + + public boolean finishAutoCancelActionMode() { + if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) { + mCurrentActionMode.finish(); + return true; + } + return false; + } + + public abstract BaseDragLayer getDragLayer(); + + public abstract T getOverviewPanel(); + + public abstract View getRootView(); + + public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info); + + public abstract void invalidateParent(ItemInfo info); + + public static BaseDraggingActivity fromContext(Context context) { + if (context instanceof BaseDraggingActivity) { + return (BaseDraggingActivity) context; + } + return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext()); + } + + public Rect getViewBounds(View v) { + int[] pos = new int[2]; + v.getLocationOnScreen(pos); + return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()); + } + + public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) { + ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions); + return activityOptions == null ? null : activityOptions.toBundle(); + } + + public abstract ActivityOptions getActivityLaunchOptions( + View v, boolean useDefaultLaunchOptions); + + public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { + if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { + Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); + return false; + } + + // Only launch using the new animation if the shortcut has not opted out (this is a + // private contract between launcher and may be ignored in the future). + boolean useLaunchAnimation = (v != null) && + !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); + Bundle optsBundle = useLaunchAnimation + ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat()) + : null; + + UserHandle user = item == null ? null : item.user; + + // Prepare intent + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (v != null) { + intent.setSourceBounds(getViewBounds(v)); + } + try { + boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW + && (item instanceof ShortcutInfo) + && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT + || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) + && !((ShortcutInfo) item).isPromise(); + if (isShortcut) { + // Shortcuts need some special checks due to legacy reasons. + startShortcutIntentSafely(intent, optsBundle, item); + } else if (user == null || user.equals(Process.myUserHandle())) { + // Could be launching some bookkeeping activity + startActivity(intent, optsBundle); + } else { + LauncherAppsCompat.getInstance(this).startActivityForProfile( + intent.getComponent(), user, intent.getSourceBounds(), optsBundle); + } + getUserEventDispatcher().logAppLaunch(v, intent); + return true; + } catch (ActivityNotFoundException|SecurityException e) { + Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); + } + return false; + } + + private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { + try { + StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); + try { + // Temporarily disable deathPenalty on all default checks. For eg, shortcuts + // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure + // is enabled by default on NYC. + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() + .penaltyLog().build()); + + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + String id = ((ShortcutInfo) info).getDeepShortcutId(); + String packageName = intent.getPackage(); + DeepShortcutManager.getInstance(this).startShortcut( + packageName, id, intent.getSourceBounds(), optsBundle, info.user); + } else { + // Could be launching some bookkeeping activity + startActivity(intent, optsBundle); + } + } finally { + StrictMode.setVmPolicy(oldPolicy); + } + } catch (SecurityException e) { + if (!onErrorStartingShortcut(intent, info)) { + throw e; + } + } + } + + protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { + return false; + } + + @Override + protected void onStart() { + super.onStart(); + + if (mOnStartCallback != null) { + mOnStartCallback.onActivityStart(this); + mOnStartCallback = null; + } + } + + public void setOnStartCallback(OnStartCallback callback) { + mOnStartCallback = callback; + } + + /** + * Callback for listening for onStart + */ + public interface OnStartCallback { + + void onActivityStart(T activity); + } +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8b6d9f825f..41bfcb7ae9 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.support.v4.graphics.ColorUtils; +import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Property; import android.util.TypedValue; @@ -44,7 +45,6 @@ import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.graphics.DrawableFactory; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.graphics.PreloadIconDrawable; @@ -65,7 +65,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed}; - private final Launcher mLauncher; + private final BaseDraggingActivity mActivity; private Drawable mIcon; private final boolean mCenterVertically; @@ -133,8 +133,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mLauncher = Launcher.getLauncher(context); - DeviceProfile grid = mLauncher.getDeviceProfile(); + mActivity = BaseDraggingActivity.fromContext(context); + DeviceProfile grid = mActivity.getDeviceProfile(); mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); TypedArray a = context.obtainStyledAttributes(attrs, @@ -164,10 +164,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mLongPressHelper = new CheckLongPressHelper(this); mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); - setAccessibilityDelegate(mLauncher.getAccessibilityDelegate()); + setEllipsize(TruncateAt.END); + setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); } + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + // Disable marques when not focused to that, so that updating text does not cause relayout. + setEllipsize(focused ? TruncateAt.MARQUEE : TruncateAt.END); + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + /** * Resets the view so it can be recycled. */ @@ -420,7 +428,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } - private void setTextAlpha(int alpha) { + public void setTextAlpha(int alpha) { super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha)); } @@ -493,10 +501,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public void applyBadgeState(ItemInfo itemInfo, boolean animate) { if (mIcon instanceof FastBitmapDrawable) { boolean wasBadged = mBadgeInfo != null; - mBadgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo); + mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo); boolean isBadged = mBadgeInfo != null; float newBadgeScale = isBadged ? 1f : 0; - mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer; + mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer; if (wasBadged || isBadged) { // Animate when a badge is first added or when it is removed. if (animate && (wasBadged ^ isBadged) && isShown()) { @@ -522,31 +530,30 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * Sets the icon for this view based on the layout direction. */ private void setIcon(Drawable icon) { - mIcon = icon; - mIcon.setBounds(0, 0, mIconSize, mIconSize); if (mIsIconVisible) { - applyCompoundDrawables(mIcon); + applyCompoundDrawables(icon); } + mIcon = icon; } public void setIconVisible(boolean visible) { mIsIconVisible = visible; - mDisableRelayout = true; - Drawable icon = mIcon; - if (!visible) { - icon = new ColorDrawable(Color.TRANSPARENT); - icon.setBounds(0, 0, mIconSize, mIconSize); - } + Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT); applyCompoundDrawables(icon); - mDisableRelayout = false; } protected void applyCompoundDrawables(Drawable icon) { + // If we had already set an icon before, disable relayout as the icon size is the + // same as before. + mDisableRelayout = mIcon != null; + + icon.setBounds(0, 0, mIconSize, mIconSize); if (mLayoutHorizontal) { setCompoundDrawablesRelative(icon, null, null, null); } else { setCompoundDrawables(null, icon, null, null); } + mDisableRelayout = false; } @Override @@ -572,15 +579,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, applyFromApplicationInfo((AppInfo) info); } else if (info instanceof ShortcutInfo) { applyFromShortcutInfo((ShortcutInfo) info); - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); - if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) { - View folderIcon = - mLauncher.getWorkspace().getHomescreenIconByItemId(info.container); - if (folderIcon != null) { - folderIcon.invalidate(); - } - } + mActivity.invalidateParent(info); } else if (info instanceof PackageItemInfo) { applyFromPackageItemInfo((PackageItemInfo) info); } diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index a3fe89a5fe..dec6cb452e 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -16,10 +16,10 @@ package com.android.launcher3; -import static com.android.launcher3.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT; +import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import android.animation.TimeInterpolator; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0bbb90c452..ed94aa4382 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -25,7 +25,6 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; -import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -47,7 +46,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; -import android.graphics.Rect; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -60,7 +58,6 @@ import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; import android.util.SparseArray; -import android.view.ActionMode; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; @@ -73,14 +70,13 @@ import android.view.animation.OvershootInterpolator; import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; +import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.LauncherAppsCompatVO; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; @@ -88,6 +84,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; @@ -139,7 +136,7 @@ import java.util.Set; /** * Default launcher application. */ -public class Launcher extends BaseActivity implements LauncherExterns, LauncherModel.Callbacks, +public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherModel.Callbacks, LauncherProviderChangeListener, WallpaperColorInfo.OnThemeChangeListener { public static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -166,10 +163,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM */ protected static final int REQUEST_LAST = 100; - // The Intent extra that defines whether to ignore the launch animation - static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = - "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; - // Type: int private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; // Type: int @@ -181,14 +174,8 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM // Type: SparseArray private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; - // When starting an action mode, setting this tag will cause the action mode to be cancelled - // automatically when user interacts with the launcher. - public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); - private LauncherStateManager mStateManager; - private boolean mIsSafeModeEnabled; - private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; // How long to wait before the new-shortcut animation automatically pans the workspace @@ -222,7 +209,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM @Thunk boolean mWorkspaceLoading = true; - private OnStartCallback mOnStartCallback; private OnResumeCallback mOnResumeCallback; private ViewOnDrawExecutor mPendingExecutor; @@ -255,7 +241,12 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM private boolean mAppLaunchSuccess; private RotationHelper mRotationHelper; - private ActionMode mCurrentActionMode; + + // Used to keep track of the swipe up state + private SharedPreferences.OnSharedPreferenceChangeListener mSharedPrefsListener = + (sharedPreferences, s) -> { + mDragLayer.setup(mDragController); + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -288,7 +279,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM initDeviceProfile(app.getInvariantDeviceProfile()); mSharedPrefs = Utilities.getPrefs(this); - mIsSafeModeEnabled = getPackageManager().isSafeMode(); + mSharedPrefs.registerOnSharedPreferenceChangeListener(mSharedPrefsListener); mIconCache = app.getIconCache(); mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); @@ -380,6 +371,9 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM getRootView().dispatchInsets(); getStateManager().reapplyState(); + // Recreate touch controllers + mDragLayer.setup(mDragController); + // TODO: We can probably avoid rebind when only screen size changed. rebindModel(); } @@ -416,10 +410,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return mStateManager; } - public LauncherAppTransitionManager getAppTransitionManager() { - return mAppTransitionManager; - } - protected void overrideTheme(boolean isDark, boolean supportsDarkText) { if (isDark) { setTheme(R.style.LauncherThemeDark); @@ -482,6 +472,22 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return mPopupDataProvider; } + @Override + public BadgeInfo getBadgeInfoForItem(ItemInfo info) { + return mPopupDataProvider.getBadgeInfoForItem(info); + } + + @Override + public void invalidateParent(ItemInfo info) { + FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv); + if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) { + View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); + if (folderIcon != null) { + folderIcon.invalidate(); + } + } + } + /** * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have * a configuration step, this allows the proper animations to run after other transitions. @@ -759,6 +765,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM } NotificationListener.removeNotificationsChangedListener(); getStateManager().moveToRestState(); + } @Override @@ -766,10 +773,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM super.onStart(); FirstFrameAnimatorHelper.setIsVisible(true); - if (mOnStartCallback != null) { - mOnStartCallback.onLauncherStart(this); - mOnStartCallback = null; - } if (mLauncherCallbacks != null) { mLauncherCallbacks.onStart(); } @@ -941,7 +944,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); // Setup the drag layer - mDragLayer.setup(this, mDragController); + mDragLayer.setup(mDragController); mWorkspace.setup(mDragController); // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the @@ -960,7 +963,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM mDragController.setMoveTarget(mWorkspace); mDropTargetBar.setup(mDragController); - mAllAppsController.setupViews(mAppsView, mHotseat); + mAllAppsController.setupViews(mAppsView); } /** @@ -1176,10 +1179,12 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return mAllAppsController; } + @Override public LauncherRootView getRootView() { return (LauncherRootView) mLauncherView; } + @Override public DragLayer getDragLayer() { return mDragLayer; } @@ -1256,11 +1261,15 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM // In all these cases, only animate if we're already on home AbstractFloatingView.closeAllOpenViews(this, isStarted()); - mStateManager.goToState(NORMAL); + if (!isInState(NORMAL)) { + // Only change state, if not already the same. This prevents cancelling any + // animations running as part of resume + mStateManager.goToState(NORMAL); + } // Reset the apps view if (!alreadyOnHome && mAppsView != null) { - mAppsView.reset(); + mAppsView.reset(isStarted() /* animate */); } if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) { @@ -1339,6 +1348,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM LauncherAppState.getInstance(this).setLauncher(null); } mRotationHelper.destroy(); + mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPrefsListener); try { mAppWidgetHost.stopListening(); @@ -1672,119 +1682,45 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM } } - private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { - try { - StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); - try { - // Temporarily disable deathPenalty on all default checks. For eg, shortcuts - // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure - // is enabled by default on NYC. - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() - .penaltyLog().build()); - - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String id = ((ShortcutInfo) info).getDeepShortcutId(); - String packageName = intent.getPackage(); - DeepShortcutManager.getInstance(this).startShortcut( - packageName, id, intent.getSourceBounds(), optsBundle, info.user); - } else { - // Could be launching some bookkeeping activity - startActivity(intent, optsBundle); - } - } finally { - StrictMode.setVmPolicy(oldPolicy); - } - } catch (SecurityException e) { - // Due to legacy reasons, direct call shortcuts require Launchers to have the - // corresponding permission. Show the appropriate permission prompt if that - // is the case. - if (intent.getComponent() == null - && Intent.ACTION_CALL.equals(intent.getAction()) - && checkSelfPermission(Manifest.permission.CALL_PHONE) != - PackageManager.PERMISSION_GRANTED) { - - setWaitingForResult(PendingRequestArgs - .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); - requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, - REQUEST_PERMISSION_CALL_PHONE); - } else { - // No idea why this was thrown. - throw e; - } - } - } - - public Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) { - ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions); - return activityOptions == null ? null : activityOptions.toBundle(); - } - @TargetApi(Build.VERSION_CODES.M) + @Override public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) { return useDefaultLaunchOptions ? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v) : mAppTransitionManager.getActivityLaunchOptions(this, v); } - public Rect getViewBounds(View v) { - int[] pos = new int[2]; - v.getLocationOnScreen(pos); - return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()); + @TargetApi(Build.VERSION_CODES.M) + @Override + protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { + // Due to legacy reasons, direct call shortcuts require Launchers to have the + // corresponding permission. Show the appropriate permission prompt if that + // is the case. + if (intent.getComponent() == null + && Intent.ACTION_CALL.equals(intent.getAction()) + && checkSelfPermission(android.Manifest.permission.CALL_PHONE) != + PackageManager.PERMISSION_GRANTED) { + + setWaitingForResult(PendingRequestArgs + .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); + requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE}, + REQUEST_PERMISSION_CALL_PHONE); + return true; + } else { + return false; + } } public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { - mAppLaunchSuccess = false; - if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { - Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); - return mAppLaunchSuccess; - } - - // Only launch using the new animation if the shortcut has not opted out (this is a - // private contract between launcher and may be ignored in the future). - boolean useLaunchAnimation = (v != null) && - !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); - Bundle optsBundle = useLaunchAnimation - ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat()) - : null; - - UserHandle user = item == null ? null : item.user; - - // Prepare intent - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (v != null) { - intent.setSourceBounds(getViewBounds(v)); - } - try { - boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW - && (item instanceof ShortcutInfo) - && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT - || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) - && !((ShortcutInfo) item).isPromise(); - if (isShortcut) { - // Shortcuts need some special checks due to legacy reasons. - startShortcutIntentSafely(intent, optsBundle, item); - } else if (user == null || user.equals(Process.myUserHandle())) { - // Could be launching some bookkeeping activity - startActivity(intent, optsBundle); - } else { - LauncherAppsCompat.getInstance(this).startActivityForProfile( - intent.getComponent(), user, intent.getSourceBounds(), optsBundle); - } - - if (v instanceof BubbleTextView) { - // This is set to the view that launched the activity that navigated the user away - // from launcher. Since there is no callback for when the activity has finished - // launching, enable the press state and keep this reference to reset the press - // state when we return to launcher. - BubbleTextView btv = (BubbleTextView) v; - btv.setStayPressed(true); - setOnResumeCallback(btv); - } - mAppLaunchSuccess = true; - getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115 - } catch (ActivityNotFoundException|SecurityException e) { - Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); + mAppLaunchSuccess = super.startActivitySafely(v, intent, item); + if (mAppLaunchSuccess && v instanceof BubbleTextView) { + // This is set to the view that launched the activity that navigated the user away + // from launcher. Since there is no callback for when the activity has finished + // launching, enable the press state and keep this reference to reset the press + // state when we return to launcher. + BubbleTextView btv = (BubbleTextView) v; + btv.setStayPressed(true); + setOnResumeCallback(btv); } return mAppLaunchSuccess; } @@ -1847,10 +1783,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM mOnResumeCallback = callback; } - public void setOnStartCallback(OnStartCallback callback) { - mOnStartCallback = callback; - } - /** * Implementation of the method from LauncherModel.Callbacks. */ @@ -2515,26 +2447,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return ((Launcher) ((ContextWrapper) context).getBaseContext()); } - @Override - public void onActionModeStarted(ActionMode mode) { - super.onActionModeStarted(mode); - mCurrentActionMode = mode; - } - - @Override - public void onActionModeFinished(ActionMode mode) { - super.onActionModeFinished(mode); - mCurrentActionMode = null; - } - - public boolean finishAutoCancelActionMode() { - if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) { - mCurrentActionMode.finish(); - return true; - } - return false; - } - /** * Callback for listening for onResume */ @@ -2542,12 +2454,4 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM void onLauncherResume(); } - - /** - * Callback for listening for onStart - */ - public interface OnStartCallback { - - void onLauncherStart(Launcher launcher); - } } diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java index 19fa3d4452..04f9b3affa 100644 --- a/src/com/android/launcher3/LauncherAppTransitionManager.java +++ b/src/com/android/launcher3/LauncherAppTransitionManager.java @@ -62,13 +62,4 @@ public class LauncherAppTransitionManager { public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) { return getDefaultActivityLaunchOptions(launcher, v); } - - /** Cancels the current Launcher transition animation */ - public void finishLauncherAnimation() { - } - - public boolean isAnimating() { - // We don't know when the activity options are being used. - return false; - } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index acad9010ee..8b7ba20b49 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -60,7 +60,7 @@ import com.android.launcher3.model.ModelWriter; import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.provider.RestoreDbTask; -import com.android.launcher3.util.NoLocaleSqliteContext; +import com.android.launcher3.util.NoLocaleSQLiteHelper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Thunk; @@ -546,7 +546,7 @@ public class LauncherProvider extends ContentProvider { /** * The class is subclassed in tests to create an in-memory db. */ - public static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { + public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback { private final Handler mWidgetHostResetHandler; private final Context mContext; private long mMaxItemId = -1; @@ -572,7 +572,7 @@ public class LauncherProvider extends ContentProvider { */ public DatabaseHelper( Context context, Handler widgetHostResetHandler, String tableName) { - super(new NoLocaleSqliteContext(context), tableName, null, SCHEMA_VERSION); + super(context, tableName, SCHEMA_VERSION); mContext = context; mWidgetHostResetHandler = widgetHostResetHandler; } diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index fc4de2d32d..b1273b64c3 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -29,6 +29,7 @@ public class LauncherRootView extends InsettableFrameLayout { private int mRightInsetBarWidth; private View mAlignedView; + private WindowStateListener mWindowStateListener; public LauncherRootView(Context context, AttributeSet attrs) { super(context, attrs); @@ -117,4 +118,31 @@ public class LauncherRootView extends InsettableFrameLayout { } } } + + public void setWindowStateListener(WindowStateListener listener) { + mWindowStateListener = listener; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (mWindowStateListener != null) { + mWindowStateListener.onWindowFocusChanged(hasWindowFocus); + } + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + if (mWindowStateListener != null) { + mWindowStateListener.onWindowVisibilityChanged(visibility); + } + } + + public interface WindowStateListener { + + void onWindowFocusChanged(boolean hasFocus); + + void onWindowVisibilityChanged(int visibility); + } } \ No newline at end of file diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 4e6bcdc2df..b1bf6ec6ef 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -40,6 +40,18 @@ import java.util.Arrays; */ public class LauncherState { + + /** + * Set of elements indicating various workspace elements which change visibility across states + * Note that workspace is not included here as in that case, we animate individual pages + */ + public static final int NONE = 0; + public static final int HOTSEAT_ICONS = 1 << 0; + public static final int HOTSEAT_EXTRA = 1 << 1; // e.g. a search box + public static final int ALL_APPS_HEADER = 1 << 2; + public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions + public static final int ALL_APPS_CONTENT = 1 << 4; + protected static final int FLAG_SHOW_SCRIM = 1 << 0; protected static final int FLAG_MULTI_PAGE = 1 << 1; protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 2; @@ -50,7 +62,7 @@ public class LauncherState { protected static final int FLAG_ALL_APPS_SCRIM = 1 << 7; protected static final int FLAG_DISABLE_INTERACTION = 1 << 8; protected static final int FLAG_OVERVIEW_UI = 1 << 9; - + protected static final int FLAG_HIDE_BACK_BUTTON = 1 << 10; protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER = new PageAlphaProvider(ACCEL_2) { @@ -66,15 +78,15 @@ public class LauncherState { * TODO: Create a separate class for NORMAL state. */ public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, - 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED); + 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON); - public static final LauncherState ALL_APPS = new AllAppsState(1); - - public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); - - public static final LauncherState OVERVIEW = new OverviewState(3); - - public static final LauncherState FAST_OVERVIEW = new FastOverviewState(4); + /** + * Various Launcher states arranged in the increasing order of UI layers + */ + public static final LauncherState SPRING_LOADED = new SpringLoadedState(1); + public static final LauncherState OVERVIEW = new OverviewState(2); + public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3); + public static final LauncherState ALL_APPS = new AllAppsState(4); public final int ordinal; @@ -131,6 +143,12 @@ public class LauncherState { */ public final boolean overviewUi; + /** + * True if the back button should be hidden when in this state (assuming no floating views are + * open, launcher has window focus, etc). + */ + public final boolean hideBackButton; + public LauncherState(int id, int containerType, int transitionDuration, int flags) { this.containerType = containerType; this.transitionDuration = transitionDuration; @@ -148,6 +166,7 @@ public class LauncherState { this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0; this.disableInteraction = (flags & FLAG_DISABLE_INTERACTION) != 0; this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0; + this.hideBackButton = (flags & FLAG_HIDE_BACK_BUTTON) != 0; this.ordinal = id; sAllStates[id] = this; @@ -161,8 +180,13 @@ public class LauncherState { return new float[] {1, 0, 0}; } - public float getHoseatAlpha(Launcher launcher) { - return 1f; + /** + * Returns 2 floats designating how much to translate overview: + * X factor is based on width, e.g. 0 is fully onscreen and 1 is fully offscreen + * Y factor is based on padding, e.g. 0 is top aligned and 0.5 is centered vertically + */ + public float[] getOverviewTranslationFactor(Launcher launcher) { + return new float[] {1f, 0f}; } public void onStateEnabled(Launcher launcher) { @@ -175,6 +199,13 @@ public class LauncherState { return launcher.getWorkspace(); } + public int getVisibleElements(Launcher launcher) { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + return HOTSEAT_ICONS; + } + return HOTSEAT_ICONS | HOTSEAT_EXTRA; + } + /** * Fraction shift in the vertical translation UI and related properties * diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 950a8ac208..7d50a5263a 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -28,8 +29,12 @@ import android.view.View; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter; import com.android.launcher3.uioverrides.UiFactory; +import java.util.ArrayList; + /** * TODO: figure out what kind of tests we can write for this * @@ -78,6 +83,7 @@ public class LauncherStateManager { private final AnimationConfig mConfig = new AnimationConfig(); private final Handler mUiHandler; private final Launcher mLauncher; + private final ArrayList mListeners = new ArrayList<>(); private StateHandler[] mStateHandlers; private LauncherState mState = NORMAL; @@ -87,8 +93,6 @@ public class LauncherStateManager { private LauncherState mRestState; - private StateListener mStateListener; - public LauncherStateManager(Launcher l) { mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; @@ -105,8 +109,12 @@ public class LauncherStateManager { return mStateHandlers; } - public void setStateListener(StateListener stateListener) { - mStateListener = stateListener; + public void addStateListener(StateListener listener) { + mListeners.add(listener); + } + + public void removeStateListener(StateListener listener) { + mListeners.remove(listener); } /** @@ -183,13 +191,13 @@ public class LauncherStateManager { mConfig.reset(); if (!animated) { - preOnStateTransitionStart(); onStateTransitionStart(state); for (StateHandler handler : getStateHandlers()) { handler.setState(state); } - if (mStateListener != null) { - mStateListener.onStateSetImmediately(state); + + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateSetImmediately(state); } onStateTransitionEnd(state); @@ -237,7 +245,6 @@ public class LauncherStateManager { protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state, AnimatorSetBuilder builder, final Runnable onCompleteRunnable) { - preOnStateTransitionStart(); for (StateHandler handler : getStateHandlers()) { builder.startTag(handler); @@ -251,16 +258,16 @@ public class LauncherStateManager { public void onAnimationStart(Animator animation) { // Change the internal state only when the transition actually starts onStateTransitionStart(state); - if (mStateListener != null) { - mStateListener.onStateTransitionStart(state); + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateTransitionStart(state); } } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - if (mStateListener != null) { - mStateListener.onStateTransitionComplete(mState); + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateTransitionComplete(state); } } @@ -277,15 +284,6 @@ public class LauncherStateManager { return mConfig.mCurrentAnimation; } - private void preOnStateTransitionStart() { - // If we are still animating to launcher from an app, - // finish it and let this state animation take over. - LauncherAppTransitionManager transitionManager = mLauncher.getAppTransitionManager(); - if (transitionManager != null) { - transitionManager.finishLauncherAnimation(); - } - } - private void onStateTransitionStart(LauncherState state) { mState.onStateDisabled(mLauncher); mState = state; @@ -351,6 +349,15 @@ public class LauncherStateManager { mConfig.reset(); } + /** + * Sets the animation as the current state animation, i.e., canceled when + * starting another animation and may block some launcher interactions while running. + */ + public void setCurrentAnimation(AnimatorSet anim) { + cancelAnimation(); + mConfig.setAnimation(anim); + } + private class StartAnimRunnable implements Runnable { private final AnimatorSet mAnim; @@ -376,12 +383,14 @@ public class LauncherStateManager { public static class AnimationConfig extends AnimatorListenerAdapter { public long duration; public boolean userControlled; + private PropertySetter mProperSetter; private AnimatorSet mCurrentAnimation; public void reset() { duration = 0; userControlled = false; + mProperSetter = null; if (mCurrentAnimation != null) { mCurrentAnimation.setDuration(0); @@ -390,6 +399,14 @@ public class LauncherStateManager { } } + public PropertySetter getProperSetter(AnimatorSetBuilder builder) { + if (mProperSetter == null) { + mProperSetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER + : new AnimatedPropertySetter(duration, builder); + } + return mProperSetter; + } + @Override public void onAnimationEnd(Animator animation) { if (mCurrentAnimation == animation) { diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 6e8cacd9c6..a1ac122d87 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -188,6 +188,10 @@ public abstract class PagedView extends ViewGrou mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density); mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density); setWillNotDraw(false); + + if (Utilities.ATLEAST_OREO) { + setDefaultFocusHighlightEnabled(false); + } } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -305,9 +309,9 @@ public abstract class PagedView extends ViewGrou } private void updatePageIndicator() { - // Update the page indicator (when we aren't reordering) if (mPageIndicator != null) { mPageIndicator.setPageDescription(getPageIndicatorDescription()); + mPageIndicator.setActiveMarker(getNextPage()); } } protected void pageBeginTransition() { diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index 6a4e93b325..7fa0e52259 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -31,11 +31,17 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.provider.Settings; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Adapter; import com.android.launcher3.graphics.IconShapeOverride; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.SettingsObserver; import com.android.launcher3.views.ButtonPreference; +import com.android.launcher3.views.HighlightableListView; /** * Settings activity for Launcher. Currently implements the following setting: Allow rotation @@ -48,6 +54,10 @@ public class SettingsActivity extends Activity { /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners"; + private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; + private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600; + private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -55,11 +65,15 @@ public class SettingsActivity extends Activity { if (savedInstanceState == null) { // Display the fragment as the main content. getFragmentManager().beginTransaction() - .replace(android.R.id.content, new LauncherSettingsFragment()) + .replace(android.R.id.content, getNewFragment()) .commit(); } } + protected PreferenceFragment getNewFragment() { + return new LauncherSettingsFragment(); + } + /** * This fragment shows the launcher preferences. */ @@ -67,9 +81,22 @@ public class SettingsActivity extends Activity { private IconBadgingObserver mIconBadgingObserver; + private String mPreferenceKey; + private boolean mPreferenceHighlighted = false; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.launcher_preference, container, false); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY); + } + getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY); addPreferencesFromResource(R.xml.launcher_preferences); @@ -100,6 +127,43 @@ public class SettingsActivity extends Activity { } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted); + } + + @Override + public void onResume() { + super.onResume(); + + Intent intent = getActivity().getIntent(); + mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY); + if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) { + getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS); + } + } + + private void highlightPreference() { + HighlightableListView list = getView().findViewById(android.R.id.list); + Preference pref = findPreference(mPreferenceKey); + Adapter adapter = list.getAdapter(); + if (adapter == null) { + return; + } + + // Find the position + int position = -1; + for (int i = adapter.getCount() - 1; i >= 0; i--) { + if (pref == adapter.getItem(i)) { + position = i; + break; + } + } + list.highlightPosition(position); + mPreferenceHighlighted = true; + } + @Override public void onDestroy() { if (mIconBadgingObserver != null) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 996095392f..68ad253700 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3254,8 +3254,7 @@ public class Workspace extends PagedView && v instanceof FolderIcon) { FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo(); for (ShortcutInfo si : ((FolderInfo) info).contents) { - folderBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider() - .getBadgeInfoForItem(si)); + folderBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(si)); } ((FolderIcon) v).setBadgeInfo(folderBadgeInfo); } diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index f6d02482b5..3a222c261c 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,79 +18,25 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; +import static com.android.launcher3.LauncherState.HOTSEAT_EXTRA; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; +import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.util.Property; import android.view.View; import com.android.launcher3.LauncherState.PageAlphaProvider; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.graphics.ViewScrim; - -/** - * A convenience class to update a view's visibility state after an alpha animation. - */ -class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { - private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; - - private View mView; - private boolean mAccessibilityEnabled; - private boolean mCanceled = false; - - public AlphaUpdateListener(View v, boolean accessibilityEnabled) { - mView = v; - mAccessibilityEnabled = accessibilityEnabled; - } - - @Override - public void onAnimationUpdate(ValueAnimator arg0) { - updateVisibility(mView, mAccessibilityEnabled); - } - - public static void updateVisibility(View view, boolean accessibilityEnabled) { - // We want to avoid the extra layout pass by setting the views to GONE unless - // accessibility is on, in which case not setting them to GONE causes a glitch. - int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE; - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { - view.setVisibility(invisibleState); - } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD - && view.getVisibility() != View.VISIBLE) { - view.setVisibility(View.VISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator arg0) { - if (mCanceled) return; - updateVisibility(mView, mAccessibilityEnabled); - } - - @Override - public void onAnimationStart(Animator arg0) { - // We want the views to be visible for animation, so fade-in/out is visible - mView.setVisibility(View.VISIBLE); - } -} +import com.android.launcher3.uioverrides.UiFactory; /** * Manages the animations between each of the workspace states. */ public class WorkspaceStateTransitionAnimation { - public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); - private final Launcher mLauncher; private final Workspace mWorkspace; @@ -107,9 +53,7 @@ public class WorkspaceStateTransitionAnimation { public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { - AnimatedPropertySetter propertySetter = - new AnimatedPropertySetter(config.duration, builder); - setWorkspaceProperty(toState, propertySetter); + setWorkspaceProperty(toState, config.getProperSetter(builder)); } public float getFinalScale() { @@ -135,10 +79,17 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, scaleAndTranslation[2], Interpolators.ZOOM_IN); - propertySetter.setViewAlpha(mLauncher.getHotseat(), state.getHoseatAlpha(mLauncher), + int elements = state.getVisibleElements(mLauncher); + float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0; + float hotseatExtraAlpha = (elements & HOTSEAT_EXTRA) != 0 ? 1 : 0; + propertySetter.setViewAlpha(mLauncher.getHotseat().getLayout(), hotseatIconsAlpha, pageAlphaProvider.interpolator); + for (View hotseatExtraContent : UiFactory.getHotseatExtraContent(mLauncher.getHotseat())) { + propertySetter.setViewAlpha(hotseatExtraContent, hotseatExtraAlpha, + pageAlphaProvider.interpolator); + } propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(), - state.getHoseatAlpha(mLauncher), pageAlphaProvider.interpolator); + hotseatIconsAlpha, pageAlphaProvider.interpolator); // Set scrim propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS, @@ -162,71 +113,4 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, pageAlpha, pageAlphaProvider.interpolator); } - - public static class PropertySetter { - - public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { - view.setAlpha(alpha); - AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext())); - } - - public void setFloat(T target, Property property, float value, - TimeInterpolator interpolator) { - property.set(target, value); - } - - public void setInt(T target, Property property, int value, - TimeInterpolator interpolator) { - property.set(target, value); - } - } - - public static class AnimatedPropertySetter extends PropertySetter { - - private final long mDuration; - private final AnimatorSetBuilder mStateAnimator; - - public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) { - mDuration = duration; - mStateAnimator = builder; - } - - @Override - public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { - if (view.getAlpha() == alpha) { - return; - } - ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); - anim.addListener(new AlphaUpdateListener( - view, isAccessibilityEnabled(view.getContext()))); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - @Override - public void setFloat(T target, Property property, float value, - TimeInterpolator interpolator) { - if (property.get(target) == value) { - return; - } - Animator anim = ObjectAnimator.ofFloat(target, property, value); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - @Override - public void setInt(T target, Property property, int value, - TimeInterpolator interpolator) { - if (property.get(target) == value) { - return; - } - Animator anim = ObjectAnimator.ofInt(target, property, value); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - private TimeInterpolator getFadeInterpolator(float finalAlpha) { - return finalAlpha == 0 ? Interpolators.DEACCEL_2 : null; - } - } } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 39a8df3919..ae41794adb 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -217,14 +217,14 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo /** * Resets the state of AllApps. */ - public void reset() { + public void reset(boolean animate) { for (int i = 0; i < mAH.length; i++) { if (mAH[i].recyclerView != null) { mAH[i].recyclerView.scrollToTop(); } } if (isHeaderVisible()) { - mHeader.reset(); + mHeader.reset(animate); } // Reset the search bar and base recycler view after transitioning home mSearchUiManager.resetSearch(); @@ -360,7 +360,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo public void onTabChanged(int pos) { mHeader.setMainActive(pos == 0); - reset(); + reset(true /* animate */); if (mAH[pos].recyclerView != null) { mAH[pos].recyclerView.bindFastScrollbar(); @@ -383,6 +383,19 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mHeader; } + public View getSearchView() { + return mSearchContainer; + } + + public View getContentView() { + return mViewPager == null ? getActiveRecyclerView() : mViewPager; + } + + public RecyclerViewFastScroller getScrollBar() { + AllAppsRecyclerView rv = getActiveRecyclerView(); + return rv == null ? null : rv.getScrollbar(); + } + public void setupHeader() { mHeader.setVisibility(View.VISIBLE); mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 13a42f1d37..9be123f973 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -1,7 +1,12 @@ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; +import static com.android.launcher3.LauncherState.ALL_APPS_HEADER; +import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; 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.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; @@ -13,16 +18,15 @@ import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.util.Themes; /** @@ -52,10 +56,7 @@ public class AllAppsTransitionController } }; - public static final float PARALLAX_COEFFICIENT = .125f; - private AllAppsContainerView mAppsView; - private Hotseat mHotseat; private final Launcher mLauncher; private final boolean mIsDarkTheme; @@ -88,7 +89,6 @@ public class AllAppsTransitionController private void onProgressAnimationStart() { // Initialize values that should not change until #onDragEnd - mHotseat.setVisibility(View.VISIBLE); mAppsView.setVisibility(View.VISIBLE); } @@ -116,14 +116,10 @@ public class AllAppsTransitionController mProgress = progress; float shiftCurrent = progress * mShiftRange; - float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f); - float alpha = 1 - workspaceHotseatAlpha; - mAppsView.setTranslationY(shiftCurrent); float hotseatTranslation = -mShiftRange + shiftCurrent; if (!mIsVerticalLayout) { - mAppsView.setAlpha(alpha); mLauncher.getHotseat().setTranslationY(hotseatTranslation); mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation); } @@ -149,6 +145,7 @@ public class AllAppsTransitionController @Override public void setState(LauncherState state) { setProgress(state.getVerticalProgress(mLauncher)); + setAlphas(state, NO_ANIM_PROPERTY_SETTER); onProgressAnimationEnd(); } @@ -161,6 +158,7 @@ public class AllAppsTransitionController AnimatorSetBuilder builder, AnimationConfig config) { float targetProgress = toState.getVerticalProgress(mLauncher); if (Float.compare(mProgress, targetProgress) == 0) { + setAlphas(toState, config.getProperSetter(builder)); // Fail fast onProgressAnimationEnd(); return; @@ -170,10 +168,24 @@ public class AllAppsTransitionController ObjectAnimator anim = ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress); anim.setDuration(config.duration); - anim.setInterpolator(interpolator); + anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); anim.addListener(getProgressAnimatorListener()); builder.play(anim); + + setAlphas(toState, config.getProperSetter(builder)); + } + + private void setAlphas(LauncherState toState, PropertySetter setter) { + int visibleElements = toState.getVisibleElements(mLauncher); + boolean hasHeader = (visibleElements & ALL_APPS_HEADER) != 0; + boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0; + boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0; + + setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, LINEAR); + setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, LINEAR); + setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, LINEAR); + mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter); } public AnimatorListenerAdapter getProgressAnimatorListener() { @@ -190,10 +202,8 @@ public class AllAppsTransitionController }; } - public void setupViews(AllAppsContainerView appsView, Hotseat hotseat) { + public void setupViews(AllAppsContainerView appsView) { mAppsView = appsView; - mHotseat = hotseat; - mHotseat.bringToFront(); mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this); } @@ -210,15 +220,12 @@ public class AllAppsTransitionController private void onProgressAnimationEnd() { if (Float.compare(mProgress, 1f) == 0) { mAppsView.setVisibility(View.INVISIBLE); - mHotseat.setVisibility(View.VISIBLE); - mAppsView.reset(); + mAppsView.reset(false /* animate */); } else if (Float.compare(mProgress, 0f) == 0) { - mHotseat.setVisibility(View.INVISIBLE); mAppsView.setVisibility(View.VISIBLE); mAppsView.onScrollUpEnd(); } else { mAppsView.setVisibility(View.VISIBLE); - mHotseat.setVisibility(View.VISIBLE); } } } diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java index a0dc5a3823..461f5b5ba5 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.anim.Interpolators.LINEAR; + import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Point; @@ -29,6 +31,7 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.launcher3.R; +import com.android.launcher3.anim.PropertySetter; public class FloatingHeaderView extends LinearLayout implements ValueAnimator.AnimatorUpdateListener { @@ -57,7 +60,7 @@ public class FloatingHeaderView extends LinearLayout implements } }; - private ViewGroup mTabLayout; + protected ViewGroup mTabLayout; private AllAppsRecyclerView mMainRV; private AllAppsRecyclerView mWorkRV; private AllAppsRecyclerView mCurrentRV; @@ -65,6 +68,8 @@ public class FloatingHeaderView extends LinearLayout implements private boolean mHeaderCollapsed; private int mSnappedScrolledY; private int mTranslationY; + + private boolean mAllowTouchForwarding; private boolean mForwardToRecyclerView; protected boolean mTabsHidden; @@ -91,7 +96,7 @@ public class FloatingHeaderView extends LinearLayout implements mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView); mParent = (ViewGroup) mMainRV.getParent(); setMainActive(true); - reset(); + reset(false); } private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) { @@ -158,12 +163,19 @@ public class FloatingHeaderView extends LinearLayout implements } } - public void reset() { - int translateTo = 0; - mAnimator.setIntValues(mTranslationY, translateTo); - mAnimator.addUpdateListener(this); - mAnimator.setDuration(150); - mAnimator.start(); + public void reset(boolean animate) { + if (mAnimator.isStarted()) { + mAnimator.cancel(); + } + if (animate) { + mAnimator.setIntValues(mTranslationY, 0); + mAnimator.addUpdateListener(this); + mAnimator.setDuration(150); + mAnimator.start(); + } else { + mTranslationY = 0; + apply(); + } mHeaderCollapsed = false; mSnappedScrolledY = -mMaxTranslation; mCurrentRV.scrollToTop(); @@ -181,6 +193,10 @@ public class FloatingHeaderView extends LinearLayout implements @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!mAllowTouchForwarding) { + mForwardToRecyclerView = false; + return super.onInterceptTouchEvent(ev); + } calcOffset(mTempOffset); ev.offsetLocation(mTempOffset.x, mTempOffset.y); mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev); @@ -208,6 +224,19 @@ public class FloatingHeaderView extends LinearLayout implements p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft(); p.y = getTop() - mCurrentRV.getTop() - mParent.getTop(); } + + public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter) { + setter.setViewAlpha(this, hasContent ? 1 : 0, LINEAR); + allowTouchForwarding(hasContent); + } + + protected void allowTouchForwarding(boolean allow) { + mAllowTouchForwarding = allow; + } + + public boolean hasVisibleContent() { + return false; + } } diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java new file mode 100644 index 0000000000..04d97a7282 --- /dev/null +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.view.View; + +/** + * A convenience class to update a view's visibility state after an alpha animation. + */ +public class AlphaUpdateListener extends AnimatorListenerAdapter implements AnimatorUpdateListener { + private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; + + private View mView; + private boolean mAccessibilityEnabled; + private boolean mCanceled = false; + + public AlphaUpdateListener(View v, boolean accessibilityEnabled) { + mView = v; + mAccessibilityEnabled = accessibilityEnabled; + } + + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + public static void updateVisibility(View view, boolean accessibilityEnabled) { + // We want to avoid the extra layout pass by setting the views to GONE unless + // accessibility is on, in which case not setting them to GONE causes a glitch. + int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE; + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { + view.setVisibility(invisibleState); + } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD + && view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator arg0) { + if (mCanceled) return; + updateVisibility(mView, mAccessibilityEnabled); + } + + @Override + public void onAnimationStart(Animator arg0) { + // We want the views to be visible for animation, so fade-in/out is visible + mView.setVisibility(View.VISIBLE); + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java index 7cd96512dc..919104886a 100644 --- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java +++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java @@ -17,6 +17,8 @@ package com.android.launcher3.anim; import android.animation.Animator; import android.animation.AnimatorSet; +import android.util.SparseArray; +import android.view.animation.Interpolator; import com.android.launcher3.LauncherAnimUtils; @@ -27,7 +29,12 @@ import java.util.ArrayList; */ public class AnimatorSetBuilder { + public static final int ANIM_VERTICAL_PROGRESS = 0; + public static final int ANIM_OVERVIEW_TRANSLATION = 1; + protected final ArrayList mAnims = new ArrayList<>(); + + private final SparseArray mInterpolators = new SparseArray<>(); private long mStartDelay = 0; /** @@ -49,4 +56,12 @@ public class AnimatorSetBuilder { anim.setStartDelay(mStartDelay); return anim; } + + public Interpolator getInterpolator(int animId, Interpolator fallback) { + return mInterpolators.get(animId, fallback); + } + + public void setInterpolator(int animId, Interpolator interpolator) { + mInterpolators.put(animId, interpolator); + } } diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java new file mode 100644 index 0000000000..1f11f7e631 --- /dev/null +++ b/src/com/android/launcher3/anim/PropertySetter.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.anim; + +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.util.Property; +import android.view.View; + +/** + * Utility class for setting a property with or without animation + */ +public class PropertySetter { + + public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); + + public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { + if (view != null) { + view.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext())); + } + } + + public void setFloat(T target, Property property, float value, + TimeInterpolator interpolator) { + property.set(target, value); + } + + public void setInt(T target, Property property, int value, + TimeInterpolator interpolator) { + property.set(target, value); + } + + public static class AnimatedPropertySetter extends PropertySetter { + + private final long mDuration; + private final AnimatorSetBuilder mStateAnimator; + + public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) { + mDuration = duration; + mStateAnimator = builder; + } + + @Override + public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { + if (view == null || view.getAlpha() == alpha) { + return; + } + ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); + anim.addListener(new AlphaUpdateListener( + view, isAccessibilityEnabled(view.getContext()))); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + @Override + public void setFloat(T target, Property property, float value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofFloat(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + @Override + public void setInt(T target, Property property, int value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofInt(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + } +} diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 28645dc73f..78ea419b82 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -51,6 +51,4 @@ abstract class BaseFlags { // When enabled shows a work profile tab in all apps public static final boolean ALL_APPS_TABS_ENABLED = true; - - public static final boolean ENABLE_TWO_SWIPE_TARGETS = true; } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 818cea76c0..5c6946c47c 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -583,6 +583,12 @@ public class DragController implements DragDriver.EventListener, TouchController } mDragObject.dragComplete = true; + if (mIsInPreDrag) { + if (dropTarget != null) { + dropTarget.onDragExit(mDragObject); + } + return; + } // Drop onto the target. boolean accepted = false; @@ -591,17 +597,15 @@ public class DragController implements DragDriver.EventListener, TouchController if (dropTarget.acceptDrop(mDragObject)) { if (flingAnimation != null) { flingAnimation.run(); - } else if (!mIsInPreDrag) { + } else { dropTarget.onDrop(mDragObject, mOptions); } accepted = true; } } final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null; - if (!mIsInPreDrag) { - mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView); - dispatchDropComplete(dropTargetAsView, accepted); - } + mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView); + dispatchDropComplete(dropTargetAsView, accepted); } private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) { diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index f5d0b24921..8519365e77 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -31,21 +31,17 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; import com.android.launcher3.DropTargetBar; -import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -53,24 +49,20 @@ import com.android.launcher3.graphics.ViewScrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.util.Thunk; -import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import java.util.ArrayList; /** * A ViewGroup that coordinates dragging across its descendants */ -public class DragLayer extends InsettableFrameLayout { +public class DragLayer extends BaseDragLayer { public static final int ANIMATION_END_DISAPPEAR = 0; public static final int ANIMATION_END_REMAIN_VISIBLE = 2; - private final int[] mTmpXY = new int[2]; - @Thunk DragController mDragController; - private Launcher mLauncher; - // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5; @@ -79,9 +71,6 @@ public class DragLayer extends InsettableFrameLayout { @Thunk View mAnchorView = null; private boolean mHoverPointClosesFolder = false; - private final Rect mHitRect = new Rect(); - - private TouchCompleteListener mTouchCompleteListener; private int mTopViewIndex; private int mChildCountOnLastUpdate = -1; @@ -89,8 +78,6 @@ public class DragLayer extends InsettableFrameLayout { // Related to adjacent page hints private final ViewGroupFocusHelper mFocusIndicatorHelper; - protected TouchController[] mControllers; - private TouchController mActiveController; /** * Used to create a new DragLayer from XML. * @@ -107,10 +94,9 @@ public class DragLayer extends InsettableFrameLayout { mFocusIndicatorHelper = new ViewGroupFocusHelper(this); } - public void setup(Launcher launcher, DragController dragController) { - mLauncher = launcher; + public void setup(DragController dragController) { mDragController = dragController; - mControllers = UiFactory.createTouchControllers(mLauncher); + mControllers = UiFactory.createTouchControllers(mActivity); } public ViewGroupFocusHelper getFocusIndicatorHelper() { @@ -123,7 +109,7 @@ public class DragLayer extends InsettableFrameLayout { } public boolean isEventOverHotseat(MotionEvent ev) { - return isEventOverView(mLauncher.getHotseat(), ev); + return isEventOverView(mActivity.getHotseat(), ev); } private boolean isEventOverFolder(Folder folder, MotionEvent ev) { @@ -131,12 +117,7 @@ public class DragLayer extends InsettableFrameLayout { } private boolean isEventOverDropTargetBar(MotionEvent ev) { - return isEventOverView(mLauncher.getDropTargetBar(), ev); - } - - public boolean isEventOverView(View view, MotionEvent ev) { - getDescendantRectRelativeToSelf(view, mHitRect); - return mHitRect.contains((int) ev.getX(), (int) ev.getY()); + return isEventOverView(mActivity.getDropTargetBar(), ev); } @Override @@ -149,66 +130,33 @@ public class DragLayer extends InsettableFrameLayout { } @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - if (mTouchCompleteListener != null) { - mTouchCompleteListener.onTouchComplete(); - } - mTouchCompleteListener = null; - } else if (action == MotionEvent.ACTION_DOWN) { - mLauncher.finishAutoCancelActionMode(); - } - return findActiveController(ev); - } - - private boolean findActiveController(MotionEvent ev) { - mActiveController = null; - - AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { - mActiveController = topView; - return true; - } - - if (mLauncher.getStateManager().getState().disableInteraction) { + protected boolean findActiveController(MotionEvent ev) { + if (mActivity.getStateManager().getState().disableInteraction) { // You Shall Not Pass!!! + mActiveController = null; return true; } - - if (mDragController.onControllerInterceptTouchEvent(ev)) { - mActiveController = mDragController; - return true; - } - - for (TouchController controller : mControllers) { - if (controller.onControllerInterceptTouchEvent(ev)) { - mActiveController = controller; - return true; - } - } - return false; + return super.findActiveController(ev); } @Override public boolean onInterceptHoverEvent(MotionEvent ev) { - if (mLauncher == null || mLauncher.getWorkspace() == null) { + if (mActivity == null || mActivity.getWorkspace() == null) { return false; } - Folder currentFolder = Folder.getOpen(mLauncher); + Folder currentFolder = Folder.getOpen(mActivity); if (currentFolder == null) { return false; } else { - AccessibilityManager accessibilityManager = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + AccessibilityManager accessibilityManager = (AccessibilityManager) + getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (accessibilityManager.isTouchExplorationEnabled()) { final int action = ev.getAction(); boolean isOverFolderOrSearchBar; switch (action) { case MotionEvent.ACTION_HOVER_ENTER: isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || - (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); if (!isOverFolderOrSearchBar) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; @@ -218,7 +166,7 @@ public class DragLayer extends InsettableFrameLayout { break; case MotionEvent.ACTION_HOVER_MOVE: isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || - (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; @@ -239,14 +187,22 @@ public class DragLayer extends InsettableFrameLayout { this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId)); } + @Override + public boolean onHoverEvent(MotionEvent ev) { + // If we've received this, we've already done the necessary handling + // in onInterceptHoverEvent. Return true to consume the event. + return false; + } + + private boolean isInAccessibleDrag() { - return mLauncher.getAccessibilityDelegate().isInAccessibleDrag(); + return mActivity.getAccessibilityDelegate().isInAccessibleDrag(); } @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { // Shortcuts can appear above folder - View topView = AbstractFloatingView.getTopOpenView(mLauncher); + View topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null) { if (child == topView) { return super.onRequestSendAccessibilityEvent(child, event); @@ -263,117 +219,23 @@ public class DragLayer extends InsettableFrameLayout { @Override public void addChildrenForAccessibility(ArrayList childrenForAccessibility) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); + View topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null) { // Only add the top view as a child for accessibility when it is open childrenForAccessibility.add(topView); if (isInAccessibleDrag()) { - childrenForAccessibility.add(mLauncher.getDropTargetBar()); + childrenForAccessibility.add(mActivity.getDropTargetBar()); } } else { super.addChildrenForAccessibility(childrenForAccessibility); } } - @Override - public boolean onHoverEvent(MotionEvent ev) { - // If we've received this, we've already done the necessary handling - // in onInterceptHoverEvent. Return true to consume the event. - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - if (mTouchCompleteListener != null) { - mTouchCompleteListener.onTouchComplete(); - } - mTouchCompleteListener = null; - } - - if (mActiveController != null) { - return mActiveController.onControllerTouchEvent(ev); - } else { - // In case no child view handled the touch event, we may not get onIntercept anymore - return findActiveController(ev); - } - } - - /** - * Determine the rect of the descendant in this DragLayer's coordinates - * - * @param descendant The descendant whose coordinates we want to find. - * @param r The rect into which to place the results. - * @return The factor by which this descendant is scaled relative to this DragLayer. - */ - public float getDescendantRectRelativeToSelf(View descendant, Rect r) { - mTmpXY[0] = 0; - mTmpXY[1] = 0; - float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); - - r.set(mTmpXY[0], mTmpXY[1], - (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()), - (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight())); - return scale; - } - - public float getLocationInDragLayer(View child, int[] loc) { - loc[0] = 0; - loc[1] = 0; - return getDescendantCoordRelativeToSelf(child, loc); - } - - public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { - return getDescendantCoordRelativeToSelf(descendant, coord, false); - } - - /** - * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's - * coordinates. - * - * @param descendant The descendant to which the passed coordinate is relative. - * @param coord The coordinate that we want mapped. - * @param includeRootScroll Whether or not to account for the scroll of the root descendant: - * sometimes this is relevant as in a child's coordinates within the root descendant. - * @return The factor by which this descendant is scaled relative to this DragLayer. Caution - * this scale factor is assumed to be equal in X and Y, and so if at any point this - * assumption fails, we will need to return a pair of scale factors. - */ - public float getDescendantCoordRelativeToSelf(View descendant, int[] coord, - boolean includeRootScroll) { - return Utilities.getDescendantCoordRelativeToAncestor(descendant, this, - coord, includeRootScroll); - } - - /** - * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. - */ - public void mapCoordInSelfToDescendant(View descendant, int[] coord) { - Utilities.mapCoordInSelfToDescendant(descendant, this, coord); - } - - public void getViewRectRelativeToSelf(View v, Rect r) { - int[] loc = new int[2]; - getLocationInWindow(loc); - int x = loc[0]; - int y = loc[1]; - - v.getLocationInWindow(loc); - int vX = loc[0]; - int vY = loc[1]; - - int left = vX - x; - int top = vY - y; - r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); - } - @Override public boolean dispatchUnhandledMove(View focused, int direction) { - // Consume the unhandled move if a container is open, to avoid switching pages underneath. - boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null; - return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction); + return super.dispatchUnhandledMove(focused, direction) + || mDragController.dispatchUnhandledMove(focused, direction); } @Override @@ -386,91 +248,6 @@ public class DragLayer extends InsettableFrameLayout { } } - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - - // Override to allow type-checking of LayoutParams. - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - @Override - protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new LayoutParams(p); - } - - public static class LayoutParams extends InsettableFrameLayout.LayoutParams { - public int x, y; - public boolean customPosition = false; - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(ViewGroup.LayoutParams lp) { - super(lp); - } - - public void setWidth(int width) { - this.width = width; - } - - public int getWidth() { - return width; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getHeight() { - return height; - } - - public void setX(int x) { - this.x = x; - } - - public int getX() { - return x; - } - - public void setY(int y) { - this.y = y; - } - - public int getY() { - return y; - } - } - - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); - if (flp instanceof LayoutParams) { - final LayoutParams lp = (LayoutParams) flp; - if (lp.customPosition) { - child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); - } - } - } - } - public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration) { @@ -709,14 +486,14 @@ public class DragLayer extends InsettableFrameLayout { public void onViewAdded(View child) { super.onViewAdded(child); updateChildIndices(); - UiFactory.onLauncherStateOrFocusChanged(mLauncher); + UiFactory.onLauncherStateOrFocusChanged(mActivity); } @Override public void onViewRemoved(View child) { super.onViewRemoved(child); updateChildIndices(); - UiFactory.onLauncherStateOrFocusChanged(mLauncher); + UiFactory.onLauncherStateOrFocusChanged(mActivity); } @Override @@ -768,32 +545,4 @@ public class DragLayer extends InsettableFrameLayout { mFocusIndicatorHelper.draw(canvas); super.dispatchDraw(canvas); } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null) { - return topView.requestFocus(direction, previouslyFocusedRect); - } else { - return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); - } - } - - @Override - public void addFocusables(ArrayList views, int direction, int focusableMode) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null) { - topView.addFocusables(views, direction); - } else { - super.addFocusables(views, direction, focusableMode); - } - } - - public void setTouchCompleteListener(TouchCompleteListener listener) { - mTouchCompleteListener = listener; - } - - public interface TouchCompleteListener { - void onTouchComplete(); - } } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 1bdd554b1f..12d7dc77a1 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -481,6 +481,8 @@ public class Folder extends AbstractFloatingView implements DragSource, openFolder.close(true); } + mIsOpen = true; + DragLayer dragLayer = mLauncher.getDragLayer(); // Just verify that the folder hasn't already been added to the DragLayer. // There was a one-off crash where the folder had a parent already. @@ -494,8 +496,6 @@ public class Folder extends AbstractFloatingView implements DragSource, } } - mIsOpen = true; - mContent.completePendingPageChanges(); if (!mDragInProgress) { // Open on the first page. diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 6c94273f6d..cb5d872ab6 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -568,7 +568,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onAdd(ShortcutInfo item, int rank) { boolean wasBadged = mBadgeInfo.hasBadge(); - mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item)); + mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item)); boolean isBadged = mBadgeInfo.hasBadge(); updateBadgeScale(wasBadged, isBadged); invalidate(); @@ -578,7 +578,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onRemove(ShortcutInfo item) { boolean wasBadged = mBadgeInfo.hasBadge(); - mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item)); + mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item)); boolean isBadged = mBadgeInfo.hasBadge(); updateBadgeScale(wasBadged, isBadged); invalidate(); diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java index c07ab084c9..c50189c1c2 100644 --- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java +++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java @@ -236,4 +236,19 @@ public abstract class FocusIndicatorHelper implements } } } + + /** + * Simple subclass which assumes that the target view is a child of the container. + */ + public static class SimpleFocusIndicatorHelper extends FocusIndicatorHelper { + + public SimpleFocusIndicatorHelper(View container) { + super(container); + } + + @Override + public void viewToRect(View v, Rect outRect) { + outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } + } } diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java index 9c80b0f085..05ae406e8b 100644 --- a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java +++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java @@ -17,13 +17,14 @@ package com.android.launcher3.keyboard; import android.graphics.Canvas; -import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ItemDecoration; import android.support.v7.widget.RecyclerView.State; import android.view.View; import android.view.View.OnFocusChangeListener; +import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper; + /** * {@link ItemDecoration} for drawing and animating focused view background. */ @@ -32,13 +33,7 @@ public class FocusedItemDecorator extends ItemDecoration { private FocusIndicatorHelper mHelper; public FocusedItemDecorator(View container) { - mHelper = new FocusIndicatorHelper(container) { - - @Override - public void viewToRect(View v, Rect outRect) { - outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); - } - }; + mHelper = new SimpleFocusIndicatorHelper(container); } public OnFocusChangeListener getFocusListener() { diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java index 4fc7d8a319..94ae39bb5c 100644 --- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java +++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java @@ -155,8 +155,8 @@ public class WorkspacePageIndicator extends View int lineWidth = (int) (availableWidth / mNumPagesFloat); int lineLeft = (int) (progress * (availableWidth - lineWidth)); int lineRight = lineLeft + lineWidth; - canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(), - mLinePaint); + canvas.drawRoundRect(lineLeft, canvas.getHeight() / 2 - mLineHeight / 2, lineRight, + canvas.getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint); } @Override diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index e427a81a1a..033fdf8889 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -745,7 +745,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra private void updateNotificationHeader() { ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag(); - BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo); + BadgeInfo badgeInfo = mLauncher.getBadgeInfoForItem(itemInfo); if (mNotificationItemView != null && badgeInfo != null) { mNotificationItemView.updateHeader( badgeInfo.getNotificationCount(), itemInfo.iconColor); diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 2cc8dfa76b..a20149e1c1 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -9,6 +9,7 @@ import android.os.Bundle; import android.view.View; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; @@ -27,7 +28,7 @@ import java.util.List; * * Example system shortcuts, defined as inner classes, include Widgets and AppInfo. */ -public abstract class SystemShortcut extends ItemInfo { +public abstract class SystemShortcut extends ItemInfo { public final int iconResId; public final int labelResId; @@ -36,10 +37,9 @@ public abstract class SystemShortcut extends ItemInfo { this.labelResId = labelResId; } - public abstract View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo); + public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo); - public static class Widgets extends SystemShortcut { + public static class Widgets extends SystemShortcut { public Widgets() { super(R.drawable.ic_widget, R.string.widget_button_text); @@ -54,17 +54,14 @@ public abstract class SystemShortcut extends ItemInfo { if (widgets == null) { return null; } - return new View.OnClickListener() { - @Override - public void onClick(View view) { - AbstractFloatingView.closeAllOpenViews(launcher); - WidgetsBottomSheet widgetsBottomSheet = - (WidgetsBottomSheet) launcher.getLayoutInflater().inflate( - R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false); - widgetsBottomSheet.populateAndShow(itemInfo); - launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, - ControlType.WIDGETS_BUTTON, view); - } + return (view) -> { + AbstractFloatingView.closeAllOpenViews(launcher); + WidgetsBottomSheet widgetsBottomSheet = + (WidgetsBottomSheet) launcher.getLayoutInflater().inflate( + R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false); + widgetsBottomSheet.populateAndShow(itemInfo); + launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, + ControlType.WIDGETS_BUTTON, view); }; } } @@ -75,18 +72,15 @@ public abstract class SystemShortcut extends ItemInfo { } @Override - public View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo) { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - Rect sourceBounds = launcher.getViewBounds(view); - Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false); - new PackageManagerHelper(launcher).startDetailsActivityForInfo( - itemInfo, sourceBounds, opts); - launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, - ControlType.APPINFO_TARGET, view); - } + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { + return (view) -> { + Rect sourceBounds = activity.getViewBounds(view); + Bundle opts = activity.getActivityLaunchOptionsAsBundle(view, false); + new PackageManagerHelper(activity).startDetailsActivityForInfo( + itemInfo, sourceBounds, opts); + activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, + ControlType.APPINFO_TARGET, view); }; } } @@ -97,28 +91,29 @@ public abstract class SystemShortcut extends ItemInfo { } @Override - public View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) && ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI); boolean isInstantApp = false; if (itemInfo instanceof com.android.launcher3.AppInfo) { com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo; - isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo); + isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo); } boolean enabled = supportsWebUI || isInstantApp; if (!enabled) { return null; } - return createOnClickListener(launcher, itemInfo); + return createOnClickListener(activity, itemInfo); } - public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) { + public View.OnClickListener createOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { return view -> { Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent( itemInfo.getTargetComponent().getPackageName()); - launcher.startActivitySafely(view, intent, itemInfo); - AbstractFloatingView.closeAllOpenViews(launcher); + activity.startActivitySafely(view, intent, itemInfo); + AbstractFloatingView.closeAllOpenViews(activity); }; } } diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java index 1a5297dc72..c809f27cdc 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; +import android.widget.Toast; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; @@ -33,7 +34,9 @@ import com.android.launcher3.Utilities; public class DeepShortcutTextView extends BubbleTextView { private final Rect mDragHandleBounds = new Rect(); private final int mDragHandleWidth; - private boolean mShouldPerformClick = true; + private boolean mShowInstructionToast = false; + + private Toast mInstructionToast; public DeepShortcutTextView(Context context) { this(context, null, 0); @@ -70,14 +73,29 @@ public class DeepShortcutTextView extends BubbleTextView { @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - // Ignore clicks on the drag handle (long clicks still start the drag). - mShouldPerformClick = !mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY()); + // Show toast if user touches the drag handle (long clicks still start the drag). + mShowInstructionToast = mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY()); } return super.onTouchEvent(ev); } @Override public boolean performClick() { - return mShouldPerformClick && super.performClick(); + if (mShowInstructionToast) { + showToast(); + return true; + } + return super.performClick(); + } + + private void showToast() { + if (mInstructionToast != null) { + mInstructionToast.cancel(); + } + CharSequence msg = Utilities.wrapForTts( + getContext().getText(R.string.long_press_shortcut_to_add), + getContext().getString(R.string.long_accessible_way_to_add_shortcut)); + mInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT); + mInstructionToast.show(); } } diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java index 89a9e2d7d1..90d3821a30 100644 --- a/src/com/android/launcher3/states/SpringLoadedState.java +++ b/src/com/android/launcher3/states/SpringLoadedState.java @@ -35,7 +35,7 @@ public class SpringLoadedState extends LauncherState { private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | - FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS; + FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS | FLAG_HIDE_BACK_BUTTON; public SpringLoadedState(int id) { super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS); diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java similarity index 50% rename from src/com/android/launcher3/util/VerticalSwipeController.java rename to src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index ae5bfd581f..a22f450e3b 100644 --- a/src/com/android/launcher3/util/VerticalSwipeController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * 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. @@ -13,92 +13,67 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.launcher3.touch; -package com.android.launcher3.util; - -import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.util.Log; import android.view.MotionEvent; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.touch.SwipeDetector.Direction; - +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.util.TouchController; /** - * Handles vertical touch gesture on the DragLayer allowing transitioning from - * {@link #mBaseState} to {@link LauncherState#ALL_APPS} and vice-versa. + * TouchController for handling state changes */ -public abstract class VerticalSwipeController extends AnimatorListenerAdapter +public abstract class AbstractStateChangeTouchController extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener { - private static final String TAG = "VerticalSwipeController"; - - private static final float RECATCH_REJECTION_FRACTION = .0875f; - private static final int SINGLE_FRAME_MS = 16; + private static final String TAG = "ASCTouchController"; + public static final float RECATCH_REJECTION_FRACTION = .0875f; + public static final int SINGLE_FRAME_MS = 16; // Progress after which the transition is assumed to be a success in case user does not fling - private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; protected final Launcher mLauncher; protected final SwipeDetector mDetector; - private final LauncherState mBaseState; - private final LauncherState mTargetState; private boolean mNoIntercept; + protected int mStartContainerType; - private AnimatorPlaybackController mCurrentAnimation; + protected LauncherState mFromState; protected LauncherState mToState; + protected AnimatorPlaybackController mCurrentAnimation; private float mStartProgress; // Ratio of transition process [0, 1] to drag displacement (px) private float mProgressMultiplier; - public VerticalSwipeController(Launcher l, LauncherState baseState) { - this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL); - } - - public VerticalSwipeController( - Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) { + public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) { mLauncher = l; mDetector = new SwipeDetector(l, this, dir); - mBaseState = baseState; - mTargetState = targetState; } - private boolean canInterceptTouch(MotionEvent ev) { - if (mCurrentAnimation != null) { - // If we are already animating from a previous state, we can intercept. - return true; - } - if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { - return false; - } - return shouldInterceptTouch(ev); - } + protected abstract boolean canInterceptTouch(MotionEvent ev); - protected abstract boolean shouldInterceptTouch(MotionEvent ev); + /** + * Initializes the {@code mFromState} and {@code mToState} and swipe direction to use for + * the detector. In can of disabling swipe, return 0. + */ + protected abstract int getSwipeDirection(MotionEvent ev); @Override - public void onAnimationCancel(Animator animation) { - if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { - Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); - mDetector.finishedScrolling(); - mCurrentAnimation = null; - } - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { @@ -121,8 +96,11 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } else { directionsToDetectScroll = getSwipeDirection(ev); + if (directionsToDetectScroll == 0) { + mNoIntercept = true; + return false; + } } - mDetector.setDetectableScrollConditions( directionsToDetectScroll, ignoreSlopWhenSettling); } @@ -135,27 +113,24 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter return mDetector.isDraggingOrSettling(); } - protected abstract int getSwipeDirection(MotionEvent ev); - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { + public final boolean onControllerTouchEvent(MotionEvent ev) { return mDetector.onTouchEvent(ev); } + protected float getShiftRange() { + return mLauncher.getAllAppsController().getShiftRange(); + } + + protected abstract float initCurrentAnimation(); + @Override public void onDragStart(boolean start) { if (mCurrentAnimation == null) { - float range = getShiftRange(); - long maxAccuracy = (long) (2 * range); - - // Build current animation - mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState; - mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, maxAccuracy); - mCurrentAnimation.getTarget().addListener(this); mStartProgress = 0; - mProgressMultiplier = - (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range; + mProgressMultiplier = initCurrentAnimation(); + + mCurrentAnimation.getTarget().addListener(this); mCurrentAnimation.dispatchOnStart(); } else { mCurrentAnimation.pause(); @@ -163,62 +138,104 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } - protected boolean isTransitionFlipped() { - return false; - } - - protected float getShiftRange() { - return mLauncher.getAllAppsController().getShiftRange(); - } - @Override public boolean onDrag(float displacement, float velocity) { float deltaProgress = mProgressMultiplier * displacement; - mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress); + updateProgress(deltaProgress + mStartProgress); return true; } + protected void updateProgress(float fraction) { + mCurrentAnimation.setPlayFraction(fraction); + } + @Override public void onDragEnd(float velocity, boolean fling) { - final long animationDuration; + final int logAction; final LauncherState targetState; final float progress = mCurrentAnimation.getProgressFraction(); if (fling) { - if (velocity < 0 ^ isTransitionFlipped()) { - targetState = mTargetState; - } else { - targetState = mBaseState; - } - animationDuration = SwipeDetector.calculateDuration(velocity, - mToState == targetState ? (1 - progress) : progress); + logAction = Touch.FLING; + targetState = + Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0 + ? mToState : mFromState; // snap to top or bottom using the release velocity } else { - if (progress > SUCCESS_TRANSITION_PROGRESS) { - targetState = mToState; - animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress); + logAction = Touch.SWIPE; + targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; + } + + + final float endProgress; + final float startProgress; + final long duration; + + if (targetState == mToState) { + endProgress = 1; + if (progress >= 1) { + duration = 0; + startProgress = 1; } else { - targetState = mToState == mTargetState ? mBaseState : mTargetState; - animationDuration = SwipeDetector.calculateDuration(velocity, progress); + startProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); + duration = SwipeDetector.calculateDuration(velocity, + endProgress - Math.max(progress, 0)); + } + } else { + endProgress = 0; + if (progress <= 0) { + duration = 0; + startProgress = 0; + } else { + startProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); + duration = SwipeDetector.calculateDuration(velocity, + Math.min(progress, 1) - endProgress); } } - mCurrentAnimation.setEndAction(() -> { - mLauncher.getStateManager().goToState(targetState, false); - onTransitionComplete(fling, targetState == mToState); - mDetector.finishedScrolling(); - mCurrentAnimation = null; - }); - - float nextFrameProgress = Utilities.boundToRange( - progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); - + mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction)); ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); - anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f); - anim.setDuration(animationDuration); - anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); + anim.setFloatValues(startProgress, endProgress); + updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling); anim.start(); } - protected abstract void onTransitionComplete(boolean wasFling, boolean stateChanged); + protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, + LauncherState targetState, float velocity, boolean isFling) { + animator.setDuration(expectedDuration) + .setInterpolator(scrollInterpolatorForVelocity(velocity)); + } + + protected int getDirectionForLog() { + return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN; + } + + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + if (targetState != mFromState) { + // Transition complete. log the action + mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, + getDirectionForLog(), + mStartContainerType, + mFromState.containerType, + mToState.containerType, + mLauncher.getWorkspace().getCurrentPage()); + } + clearState(); + mLauncher.getStateManager().goToState(targetState, false /* animated */); + } + + protected void clearState() { + mCurrentAnimation = null; + mDetector.finishedScrolling(); + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) { + Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); + clearState(); + } + } } diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index f10a695c40..6f012f69a5 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; +import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -78,8 +79,8 @@ public class ItemLongClickListener { Launcher launcher = Launcher.getLauncher(v.getContext()); if (!canStartDrag(launcher)) return false; // When we have exited all apps or are in transition, disregard long clicks - if (!launcher.isInState(LauncherState.ALL_APPS) || - launcher.getWorkspace().isSwitchingState()) return false; + if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false; + if (launcher.getWorkspace().isSwitchingState()) return false; // Start the drag final DragController dragController = launcher.getDragController(); diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index df11686b2c..2f9cf3aab6 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -102,23 +102,30 @@ public class WorkspaceTouchListener implements OnTouchListener, Runnable { // Inform the workspace to cancel touch handling ev.setAction(ACTION_CANCEL); mWorkspace.onTouchEvent(ev); + ev.setAction(action); mLongPressState = STATE_COMPLETED; } + final boolean result; if (mLongPressState == STATE_COMPLETED) { // We have handled the touch, so workspace does not need to know anything anymore. - return true; + result = true; } else if (mLongPressState == STATE_REQUESTED) { mWorkspace.onTouchEvent(ev); - if (action == ACTION_UP || action == ACTION_CANCEL || mWorkspace.isHandlingTouch()) { + if (mWorkspace.isHandlingTouch()) { cancelLongPress(); } - return true; + + result = true; } else { // We don't want to handle touch, let workspace handle it as usual. - return false; + result = false; } + if (action == ACTION_UP || action == ACTION_CANCEL) { + cancelLongPress(); + } + return result; } private void cancelLongPress() { diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java index 601a5ab80d..4485427f57 100644 --- a/src/com/android/launcher3/util/InstantAppResolver.java +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -22,7 +22,6 @@ import android.content.pm.PackageManager; import android.util.Log; import com.android.launcher3.AppInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -47,8 +46,8 @@ public class InstantAppResolver { return false; } - public boolean isInstantApp(Launcher launcher, String packageName) { - PackageManager packageManager = launcher.getPackageManager(); + public boolean isInstantApp(Context context, String packageName) { + PackageManager packageManager = context.getPackageManager(); try { return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo); } catch (PackageManager.NameNotFoundException e) { diff --git a/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java new file mode 100644 index 0000000000..05a7d27434 --- /dev/null +++ b/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util; + +import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS; + +import static com.android.launcher3.Utilities.ATLEAST_P; + +import android.content.Context; +import android.content.ContextWrapper; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteDatabase.OpenParams; +import android.database.sqlite.SQLiteOpenHelper; + +/** + * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by + * A context wrapper which creates databases without support for localized collators. + */ +public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper { + + public NoLocaleSQLiteHelper(Context context, String name, int version) { + super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version); + if (ATLEAST_P) { + setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build()); + } + } + + private static class NoLocalContext extends ContextWrapper { + public NoLocalContext(Context base) { + super(base); + } + + @Override + public SQLiteDatabase openOrCreateDatabase( + String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) { + return super.openOrCreateDatabase( + name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler); + } + } +} diff --git a/src/com/android/launcher3/util/NoLocaleSqliteContext.java b/src/com/android/launcher3/util/NoLocaleSqliteContext.java deleted file mode 100644 index c8a5ffb1a0..0000000000 --- a/src/com/android/launcher3/util/NoLocaleSqliteContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.android.launcher3.util; - -import android.content.Context; -import android.content.ContextWrapper; -import android.database.DatabaseErrorHandler; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDatabase.CursorFactory; - -/** - * A context wrapper which creates databases without support for localized collators. - */ -public class NoLocaleSqliteContext extends ContextWrapper { - - public NoLocaleSqliteContext(Context context) { - super(context); - } - - @Override - public SQLiteDatabase openOrCreateDatabase( - String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) { - return super.openOrCreateDatabase( - name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler); - } -} diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java index 9084bfbd32..44c1762c34 100644 --- a/src/com/android/launcher3/util/SQLiteCacheHelper.java +++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java @@ -92,10 +92,10 @@ public abstract class SQLiteCacheHelper { /** * A private inner class to prevent direct DB access. */ - private class MySQLiteOpenHelper extends SQLiteOpenHelper { + private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper { public MySQLiteOpenHelper(Context context, String name, int version) { - super(new NoLocaleSqliteContext(context), name, null, version); + super(context, name, version); } @Override diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java new file mode 100644 index 0000000000..489e59e75b --- /dev/null +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.views; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.InsettableFrameLayout; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.TouchController; + +import java.util.ArrayList; + +/** + * A viewgroup with utility methods for drag-n-drop and touch interception + */ +public abstract class BaseDragLayer extends InsettableFrameLayout { + + protected final int[] mTmpXY = new int[2]; + protected final Rect mHitRect = new Rect(); + + protected final T mActivity; + + protected TouchController[] mControllers; + protected TouchController mActiveController; + private TouchCompleteListener mTouchCompleteListener; + + public BaseDragLayer(Context context, AttributeSet attrs) { + super(context, attrs); + mActivity = (T) BaseActivity.fromContext(context); + } + + + public boolean isEventOverView(View view, MotionEvent ev) { + getDescendantRectRelativeToSelf(view, mHitRect); + return mHitRect.contains((int) ev.getX(), (int) ev.getY()); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (mTouchCompleteListener != null) { + mTouchCompleteListener.onTouchComplete(); + } + mTouchCompleteListener = null; + } else if (action == MotionEvent.ACTION_DOWN) { + mActivity.finishAutoCancelActionMode(); + } + return findActiveController(ev); + } + + protected boolean findActiveController(MotionEvent ev) { + mActiveController = null; + + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { + mActiveController = topView; + return true; + } + + for (TouchController controller : mControllers) { + if (controller.onControllerInterceptTouchEvent(ev)) { + mActiveController = controller; + return true; + } + } + return false; + } + + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + // Shortcuts can appear above folder + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + if (child == topView) { + return super.onRequestSendAccessibilityEvent(child, event); + } + // Skip propagating onRequestSendAccessibilityEvent for all other children + // which are not topView + return false; + } + return super.onRequestSendAccessibilityEvent(child, event); + } + + @Override + public void addChildrenForAccessibility(ArrayList childrenForAccessibility) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + // Only add the top view as a child for accessibility when it is open + childrenForAccessibility.add(topView); + } else { + super.addChildrenForAccessibility(childrenForAccessibility); + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (mTouchCompleteListener != null) { + mTouchCompleteListener.onTouchComplete(); + } + mTouchCompleteListener = null; + } + + if (mActiveController != null) { + return mActiveController.onControllerTouchEvent(ev); + } else { + // In case no child view handled the touch event, we may not get onIntercept anymore + return findActiveController(ev); + } + } + + /** + * Determine the rect of the descendant in this DragLayer's coordinates + * + * @param descendant The descendant whose coordinates we want to find. + * @param r The rect into which to place the results. + * @return The factor by which this descendant is scaled relative to this DragLayer. + */ + public float getDescendantRectRelativeToSelf(View descendant, Rect r) { + mTmpXY[0] = 0; + mTmpXY[1] = 0; + float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); + + r.set(mTmpXY[0], mTmpXY[1], + (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()), + (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight())); + return scale; + } + + public float getLocationInDragLayer(View child, int[] loc) { + loc[0] = 0; + loc[1] = 0; + return getDescendantCoordRelativeToSelf(child, loc); + } + + public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { + return getDescendantCoordRelativeToSelf(descendant, coord, false); + } + + /** + * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's + * coordinates. + * + * @param descendant The descendant to which the passed coordinate is relative. + * @param coord The coordinate that we want mapped. + * @param includeRootScroll Whether or not to account for the scroll of the root descendant: + * sometimes this is relevant as in a child's coordinates within the root descendant. + * @return The factor by which this descendant is scaled relative to this DragLayer. Caution + * this scale factor is assumed to be equal in X and Y, and so if at any point this + * assumption fails, we will need to return a pair of scale factors. + */ + public float getDescendantCoordRelativeToSelf(View descendant, int[] coord, + boolean includeRootScroll) { + return Utilities.getDescendantCoordRelativeToAncestor(descendant, this, + coord, includeRootScroll); + } + + /** + * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. + */ + public void mapCoordInSelfToDescendant(View descendant, int[] coord) { + Utilities.mapCoordInSelfToDescendant(descendant, this, coord); + } + + public void getViewRectRelativeToSelf(View v, Rect r) { + int[] loc = new int[2]; + getLocationInWindow(loc); + int x = loc[0]; + int y = loc[1]; + + v.getLocationInWindow(loc); + int vX = loc[0]; + int vY = loc[1]; + + int left = vX - x; + int top = vY - y; + r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); + } + + @Override + public boolean dispatchUnhandledMove(View focused, int direction) { + // Consume the unhandled move if a container is open, to avoid switching pages underneath. + return AbstractFloatingView.getTopOpenView(mActivity) != null; + } + + @Override + protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + return topView.requestFocus(direction, previouslyFocusedRect); + } else { + return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); + } + } + + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + topView.addFocusables(views, direction); + } else { + super.addFocusables(views, direction, focusableMode); + } + } + + public void setTouchCompleteListener(TouchCompleteListener listener) { + mTouchCompleteListener = listener; + } + + public interface TouchCompleteListener { + void onTouchComplete(); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + // Override to allow type-checking of LayoutParams. + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + public static class LayoutParams extends InsettableFrameLayout.LayoutParams { + public int x, y; + public boolean customPosition = false; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.LayoutParams lp) { + super(lp); + } + + public void setWidth(int width) { + this.width = width; + } + + public int getWidth() { + return width; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getHeight() { + return height; + } + + public void setX(int x) { + this.x = x; + } + + public int getX() { + return x; + } + + public void setY(int y) { + this.y = y; + } + + public int getY() { + return y; + } + } + + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); + if (flp instanceof LayoutParams) { + final LayoutParams lp = (LayoutParams) flp; + if (lp.customPosition) { + child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); + } + } + } + } +} diff --git a/src/com/android/launcher3/views/BottomUserEducationView.java b/src/com/android/launcher3/views/BottomUserEducationView.java index ba78cf684b..a291fc6059 100644 --- a/src/com/android/launcher3/views/BottomUserEducationView.java +++ b/src/com/android/launcher3/views/BottomUserEducationView.java @@ -22,11 +22,15 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.TouchDelegate; import android.view.View; +import android.view.accessibility.AccessibilityEvent; + import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; +import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; + public class BottomUserEducationView extends AbstractSlideInView implements Insettable { private static final String KEY_SHOWED_BOTTOM_USER_EDUCATION = "showed_bottom_user_education"; @@ -90,6 +94,10 @@ public class BottomUserEducationView extends AbstractSlideInView implements Inse // close action. mLauncher.getSharedPrefs().edit() .putBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, true).apply(); + sendCustomAccessibilityEvent( + BottomUserEducationView.this, + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, + getContext().getString(R.string.bottom_work_tab_user_education_closed)); } } diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java index 01b63bec47..a11a8c5377 100644 --- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java +++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Region; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.widget.TextView; diff --git a/src/com/android/launcher3/views/HighlightableListView.java b/src/com/android/launcher3/views/HighlightableListView.java new file mode 100644 index 0000000000..7da979fe14 --- /dev/null +++ b/src/com/android/launcher3/views/HighlightableListView.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.views; + +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.v4.graphics.ColorUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HeaderViewListAdapter; +import android.widget.ListAdapter; +import android.widget.ListView; + +import com.android.launcher3.R; +import com.android.launcher3.util.Themes; + +import java.util.ArrayList; + +/** + * Extension of list view with support for element highlighting. + */ +public class HighlightableListView extends ListView { + + private int mPosHighlight = -1; + private boolean mColorAnimated = false; + + public HighlightableListView(Context context) { + super(context); + } + + public HighlightableListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public HighlightableListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(new HighLightAdapter(adapter)); + } + + public void highlightPosition(int pos) { + if (mPosHighlight == pos) { + return; + } + + mColorAnimated = false; + mPosHighlight = pos; + setSelection(mPosHighlight); + + int start = getFirstVisiblePosition(); + int end = getLastVisiblePosition(); + if (start <= mPosHighlight && mPosHighlight <= end) { + highlightView(getChildAt(mPosHighlight - start)); + } + } + + private void highlightView(View view) { + if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) { + // already highlighted + } else { + view.setTag(R.id.view_highlighted, true); + view.setTag(R.id.view_unhighlight_background, view.getBackground()); + view.setBackground(getHighlightBackground()); + view.postDelayed(() -> { + mPosHighlight = -1; + unhighlightView(view); + }, 15000L); + } + } + + private void unhighlightView(View view) { + if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) { + Object background = view.getTag(R.id.view_unhighlight_background); + if (background instanceof Drawable) { + view.setBackground((Drawable) background); + } + view.setTag(R.id.view_unhighlight_background, null); + view.setTag(R.id.view_highlighted, false); + } + } + + private class HighLightAdapter extends HeaderViewListAdapter { + public HighLightAdapter(ListAdapter adapter) { + super(new ArrayList<>(), new ArrayList<>(), adapter); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + + if (position == mPosHighlight) { + highlightView(view); + } else { + unhighlightView(view); + } + return view; + } + } + + private ColorDrawable getHighlightBackground() { + int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(getContext()), 26); + if (mColorAnimated) { + return new ColorDrawable(color); + } + mColorAnimated = true; + ColorDrawable bg = new ColorDrawable(Color.WHITE); + ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color); + anim.setEvaluator(new ArgbEvaluator()); + anim.setDuration(200L); + anim.setRepeatMode(ValueAnimator.REVERSE); + anim.setRepeatCount(4); + anim.start(); + return bg; + } +} diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 21b6773559..dc86aec463 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -288,8 +288,8 @@ public class OptionsPopupView extends AbstractFloatingView lp.y = Utilities.boundToRange((int) (y - height / 2), insets.top + margin, maxHeight - insets.bottom - height - margin); - launcher.getDragLayer().addView(view); view.animateOpen(); + launcher.getDragLayer().addView(view); } public static boolean onWidgetsClicked(Launcher launcher) { diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index 9d74218e04..12859c78f4 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -47,7 +47,7 @@ import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener; +import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener; import java.util.ArrayList; diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java index 2acd29bc69..49a9dc7c93 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java @@ -25,7 +25,6 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; /** @@ -33,8 +32,9 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; */ public class AllAppsState extends LauncherState { - private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY - | FLAG_SHOW_SCRIM | FLAG_ALL_APPS_SCRIM; + private static final float PARALLAX_COEFFICIENT = .125f; + + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_ALL_APPS_SCRIM; private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) { @Override @@ -63,8 +63,8 @@ public class AllAppsState extends LauncherState { } @Override - public float getHoseatAlpha(Launcher launcher) { - return 0; + public int getVisibleElements(Launcher launcher) { + return ALL_APPS_HEADER | ALL_APPS_CONTENT; } @Override @@ -75,8 +75,7 @@ public class AllAppsState extends LauncherState { @Override public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { return new float[] { 1f, 0, - -launcher.getAllAppsController().getShiftRange() - * AllAppsTransitionController.PARALLAX_COEFFICIENT}; + -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT}; } @Override diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java index 76b7e0d81e..e495477f57 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java @@ -1,19 +1,3 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.ALL_APPS; @@ -21,31 +5,34 @@ import static com.android.launcher3.LauncherState.NORMAL; import android.view.MotionEvent; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; +import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; /** - * Extension of {@link VerticalSwipeController} to switch between NORMAL and ALL_APPS state. + * TouchController to switch between NORMAL and ALL_APPS state. */ -public class AllAppsSwipeController extends VerticalSwipeController { - - private int mStartContainerType; +public class AllAppsSwipeController extends AbstractStateChangeTouchController { public AllAppsSwipeController(Launcher l) { - super(l, NORMAL); + super(l, SwipeDetector.VERTICAL); } @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { + protected boolean canInterceptTouch(MotionEvent ev) { + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { // Don't listen for the swipe gesture if we are already in some other state. return false; } - if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) { return false; } @@ -56,8 +43,12 @@ public class AllAppsSwipeController extends VerticalSwipeController { protected int getSwipeDirection(MotionEvent ev) { if (mLauncher.isInState(ALL_APPS)) { mStartContainerType = ContainerType.ALLAPPS; + mFromState = ALL_APPS; + mToState = NORMAL; return SwipeDetector.DIRECTION_NEGATIVE; } else { + mFromState = NORMAL; + mToState = ALL_APPS; mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE; return SwipeDetector.DIRECTION_POSITIVE; @@ -65,14 +56,14 @@ public class AllAppsSwipeController extends VerticalSwipeController { } @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logActionOnContainer( - wasFling ? Touch.FLING : Touch.SWIPE, - mLauncher.isInState(ALL_APPS) ? Direction.UP : Direction.DOWN, - mStartContainerType, - mLauncher.getWorkspace().getCurrentPage()); - } + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; + float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; + float totalShift = endVerticalShift - startVerticalShift; + return 1 / totalShift; } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java index 3dfbc401dd..8def0d3ccc 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -16,16 +16,8 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; -import android.graphics.Rect; -import android.view.View; -import android.view.accessibility.AccessibilityNodeInfo; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; /** diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index a9694a74c9..2f1de7bf64 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -16,16 +16,20 @@ package com.android.launcher3.uioverrides; +import android.view.View; import android.view.View.AccessibilityDelegate; +import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; +import com.android.launcher3.R; import com.android.launcher3.util.TouchController; public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { - return new TouchController[] {new AllAppsSwipeController(launcher)}; + return new TouchController[] { + launcher.getDragController(), new AllAppsSwipeController(launcher)}; } public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { @@ -44,4 +48,10 @@ public class UiFactory { public static void onStart(Launcher launcher) { } public static void onTrimMemory(Launcher launcher, int level) { } + + public static View[] getHotseatExtraContent(Hotseat hotseat) { + return new View[] { + hotseat.findViewById(R.id.drag_indicator), + }; + } }