diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index bb03f50763..c24850d847 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -87,9 +87,11 @@ android:process=":wallpaper_chooser" android:permission="android.permission.BIND_JOB_SERVICE" /> - + diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6ef78285f2..c3ddfd62b8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -71,7 +71,7 @@ android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:windowSoftInputMode="adjustPan" - android:screenOrientation="nosensor" + android:screenOrientation="unspecified" android:configChanges="keyboard|keyboardHidden|navigation" android:resizeableActivity="true" android:resumeWhilePausing="true" diff --git a/proguard.flags b/proguard.flags index b8cade5726..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); @@ -102,6 +102,11 @@ public (...); } +# InstantAppResolver +-keep class com.android.quickstep.InstantAppResolverImpl { + public (...); +} + -keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** { *; } diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 02b4379bf6..8b28597dd1 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -36,13 +36,32 @@ android:restoreAnyVersion="true" android:supportsRtl="true" > - + + + + + + android:exported="true" + android:excludeFromRecents="true" /> + + + + + + + diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index d5859a7a54..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 new file mode 100644 index 0000000000..b3fc4ed58c --- /dev/null +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -0,0 +1,31 @@ + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 9f4f8a153f..89e057148f 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -14,18 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. --> - + android:visibility="invisible" + android:focusableInTouchMode="true" > - - - \ No newline at end of file + \ No newline at end of file diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 91b6aa3361..0ac2b11030 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -13,12 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - \ No newline at end of file + \ No newline at end of file diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml index 6e3fb4fbdb..b8466652e5 100644 --- a/quickstep/res/layout/task_menu.xml +++ b/quickstep/res/layout/task_menu.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - \ No newline at end of file + \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 0956048429..b751e0d9c9 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -16,8 +16,6 @@ - 24dp - 24dp 48dp 12dp @@ -25,19 +23,17 @@ 20dp 10dp + + 600dp 500dp 250dp - -24dp - - - 90dp - 25dp 80dp - 120dp + 140dp 80dp 150% diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml index ba99d81c07..2bd9f8f6f3 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -16,5 +16,7 @@ com.android.launcher3.LauncherAppTransitionManagerImpl + + com.android.quickstep.InstantAppResolverImpl 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 0fe29e35dc..5a090d9e4c 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,11 +16,11 @@ 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.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; @@ -31,13 +31,16 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityOptions; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Matrix; 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; @@ -47,14 +50,18 @@ import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.InsettableFrameLayout.LayoutParams; import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.graphics.DrawableFactory; +import com.android.launcher3.shortcuts.DeepShortcutTextView; +import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.quickstep.RecentsAnimationInterpolator; import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds; -import com.android.quickstep.RecentsView; -import com.android.quickstep.TaskView; +import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.recents.model.Task; +import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.ActivityCompat; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; @@ -73,13 +80,16 @@ 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 = "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; + private static final int APP_LAUNCH_DURATION = 500; + // Use a shorter duration for x or y translation to create a curve effect + private static final int APP_LAUNCH_CURVED_DURATION = 233; private static final int RECENTS_LAUNCH_DURATION = 336; - private static final int LAUNCHER_RESUME_START_DELAY = 150; + private static final int LAUNCHER_RESUME_START_DELAY = 100; private static final int CLOSING_TRANSITION_DURATION_MS = 350; // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down. @@ -88,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; @@ -96,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); @@ -124,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. @@ -152,57 +150,28 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) { if (hasControlRemoteAppTransitionPermission()) { 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(v, 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; } }; - return ActivityOptionsCompat.makeRemoteAnimation( - new RemoteAnimationAdapterCompat(runner, 500, 380)); + 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)); } catch (NoClassDefFoundError e) { // Gracefully fall back to default launch options if the user's platform doesn't // have the latest changes. @@ -212,19 +181,44 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag } /** - * Composes the animations for a launch from the recents list if possible. + * Try to find a TaskView that corresponds with the component of the launched view. + * + * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation. + * 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 LauncherTransitionAnimator composeRecentsLaunchAnimator(View v, - RemoteAnimationTargetCompat[] targets) { - // Ensure recents is actually visible - if (!mLauncher.isInState(LauncherState.OVERVIEW)) { - return null; + private TaskView findTaskViewToLaunch( + BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { + if (v instanceof TaskView) { + return (TaskView) v; + } + 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) { + for (int i = 0; i < recentsView.getChildCount(); i++) { + TaskView taskView = (TaskView) recentsView.getPageAt(i); + if (recentsView.isTaskViewVisible(taskView)) { + Task task = taskView.getTask(); + if (componentName.equals(task.key.getComponent())) { + return taskView; + } + } + } + } } + if (targets == null) { + return null; + } // Resolve the opening task id int openingTaskId = -1; for (RemoteAnimationTargetCompat target : targets) { - if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) { + if (target.mode == MODE_OPENING) { openingTaskId = target.taskId; break; } @@ -237,15 +231,56 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag // If the opening task id is not currently visible in overview, then fall back to normal app // icon launch animation - RecentsView recentsView = mLauncher.getOverviewPanel(); TaskView taskView = recentsView.getTaskView(openingTaskId); 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 - return new LauncherTransitionAnimator(getRecentsLauncherAnimator(recentsView, taskView), - getRecentsWindowAnimator(taskView, targets)); + Animator launcherAnim; + final AnimatorListenerAdapter windowAnimEndListener; + if (launcherClosing) { + launcherAnim = getRecentsLauncherAnimator(recentsView, taskView); + // Make sure recents gets fixed up by resetting task alphas and scales, etc. + windowAnimEndListener = mReapplyStateListener; + } else { + AnimatorPlaybackController controller = + mLauncher.getStateManager() + .createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION); + controller.dispatchOnStart(); + launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION); + windowAnimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLauncher.getStateManager().goToState(NORMAL, false); + } + }; + } + + Animator windowAnim = getRecentsWindowAnimator(taskView, skipLauncherChanges, targets); + windowAnim.addListener(windowAnimEndListener); + return new Animator[] {launcherAnim, windowAnim}; } /** @@ -260,15 +295,16 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag int launchedTaskIndex = recentsView.indexOfChild(v); int centerTaskIndex = recentsView.getCurrentPage(); boolean launchingCenterTask = launchedTaskIndex == centerTaskIndex; + boolean isRtl = recentsView.isRtl(); if (launchingCenterTask) { - if (launchedTaskIndex - 1 >= recentsView.getFirstTaskIndex()) { + if (launchedTaskIndex - 1 >= 0) { TaskView adjacentPage1 = (TaskView) recentsView.getPageAt(launchedTaskIndex - 1); ObjectAnimator adjacentTask1ScaleAndTranslate = LauncherAnimUtils.ofPropertyValuesHolder(adjacentPage1, new PropertyListBuilder() .scale(adjacentPage1.getScaleX() * mRecentsScale) .translationY(mRecentsTransY) - .translationX(mIsRtl ? mRecentsTransX : -mRecentsTransX) + .translationX(isRtl ? mRecentsTransX : -mRecentsTransX) .build()); launcherAnimator.play(adjacentTask1ScaleAndTranslate); } @@ -279,33 +315,31 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag new PropertyListBuilder() .scale(adjacentTask2.getScaleX() * mRecentsScale) .translationY(mRecentsTransY) - .translationX(mIsRtl ? -mRecentsTransX : mRecentsTransX) + .translationX(isRtl ? -mRecentsTransX : mRecentsTransX) .build()); launcherAnimator.play(adjacentTask2ScaleAndTranslate); } - } else if (centerTaskIndex >= recentsView.getFirstTaskIndex()) { + } 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(mIsRtl ? -translationX : translationX) + .translationX(isRtl ? -translationX : translationX) .build()); - launcherAnimator.play(centerTaskParallaxToRight); + launcherAnimator.play(centerTaskParallaxOffscreen); int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - launchedTaskIndex); - if (otherAdjacentTaskIndex >= recentsView.getFirstTaskIndex() + if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < recentsView.getPageCount()) { TaskView otherAdjacentTask = (TaskView) recentsView.getPageAt( otherAdjacentTaskIndex); - ObjectAnimator otherAdjacentTaskParallaxToRight = + ObjectAnimator otherAdjacentTaskParallaxOffscreen = LauncherAnimUtils.ofPropertyValuesHolder(otherAdjacentTask, new PropertyListBuilder() - .translationX(otherAdjacentTask.getTranslationX() - + (mIsRtl ? -translationX : translationX)) + .translationX(isRtl ? -translationX : translationX) .build()); - launcherAnimator.play(otherAdjacentTaskParallaxToRight); + launcherAnimator.play(otherAdjacentTaskParallaxOffscreen); } } @@ -314,7 +348,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag launcherAnimator.play(allAppsSlideOut); Workspace workspace = mLauncher.getWorkspace(); - float[] workspaceScaleAndTranslation = LauncherState.NORMAL + float[] workspaceScaleAndTranslation = NORMAL .getWorkspaceScaleAndTranslation(mLauncher); Animator recenterWorkspace = LauncherAnimUtils.ofPropertyValuesHolder( workspace, new PropertyListBuilder() @@ -324,9 +358,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag launcherAnimator.play(recenterWorkspace); CellLayout currentWorkspacePage = (CellLayout) workspace.getPageAt( workspace.getCurrentPage()); - Animator hideWorkspaceScrim = ObjectAnimator.ofInt( - currentWorkspacePage.getScrimBackground(), DRAWABLE_ALPHA, 0); - launcherAnimator.play(hideWorkspaceScrim); launcherAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); launcherAnimator.setDuration(RECENTS_LAUNCH_DURATION); @@ -337,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, + private ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipLauncherChanges, RemoteAnimationTargetCompat[] targets) { Rect taskViewBounds = new Rect(); mDragLayer.getDescendantRectRelativeToSelf(v, taskViewBounds); @@ -373,22 +404,23 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag final float percent = animation.getAnimatedFraction(); TaskWindowBounds tw = recentsInterpolator.interpolate(percent); - v.setScaleX(tw.taskScale); - v.setScaleY(tw.taskScale); - v.setTranslationX(tw.taskX); - v.setTranslationY(tw.taskY); - // Defer fading out the view until after the app window gets faded in - v.setAlpha(getValue(1f, 0f, 75, 75, - appAnimator.getDuration() * percent, Interpolators.LINEAR)); + if (!skipLauncherChanges) { + v.setScaleX(tw.taskScale); + v.setScaleY(tw.taskScale); + v.setTranslationX(tw.taskX); + v.setTranslationY(tw.taskY); + // Defer fading out the view until after the app window gets faded in + v.setAlpha(getValue(1f, 0f, 75, 75, + appAnimator.getDuration() * percent, Interpolators.LINEAR)); + } matrix.setScale(tw.winScale, tw.winScale); matrix.postTranslate(tw.winX, tw.winY); 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(); @@ -401,7 +433,10 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag matrix.postTranslate(target.position.x, target.position.y); t.setMatrix(target.leash, matrix); t.setWindowCrop(target.leash, crop); - t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface)); + + if (!skipLauncherChanges) { + t.deferTransactionUntil(target.leash, surface, frameNumber); + } } if (isFirstFrame) { t.show(target.leash); @@ -413,25 +448,9 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag isFirstFrame = false; } }); - appAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Make sure recents gets fixed up by resetting task alphas and scales, etc. - mLauncher.getStateManager().reapplyState(); - } - }); 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. */ @@ -462,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]); @@ -475,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]); @@ -489,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; } @@ -497,33 +533,56 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag * @return Animator that controls the icon used to launch the target. */ private AnimatorSet getIconAnimator(View v) { - boolean isBubbleTextView = v instanceof BubbleTextView; + final boolean isBubbleTextView = v instanceof BubbleTextView; mFloatingView = new View(mLauncher); if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) { // Create a copy of the app icon - ItemInfoWithIcon info = (ItemInfoWithIcon) v.getTag(); - FastBitmapDrawable d = DrawableFactory.get(mLauncher).newIcon(info); - d.setIsDisabled(info.isDisabled()); - mFloatingView.setBackground(d); + mFloatingView.setBackground( + DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag())); } // Position the floating view exactly on top of the original Rect rect = new Rect(); - mDragLayer.getDescendantRectRelativeToSelf(v, rect); - int viewLocationStart = mIsRtl - ? mDeviceProfile.widthPx - rect.right - : rect.left; + 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); + } + int viewLocationLeft = rect.left; int viewLocationTop = rect.top; - if (isBubbleTextView) { - ((BubbleTextView) v).getIconBounds(rect); + float startScale = 1f; + if (isBubbleTextView && !fromDeepShortcutView) { + BubbleTextView btv = (BubbleTextView) v; + btv.getIconBounds(rect); + Drawable dr = btv.getIcon(); + if (dr instanceof FastBitmapDrawable) { + startScale = ((FastBitmapDrawable) dr).getAnimatedScale(); + } + } 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); @@ -544,8 +603,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag // Adjust the duration to change the "curve" of the app icon to the center. boolean isBelowCenterY = lp.topMargin < centerY; - x.setDuration(isBelowCenterY ? 500 : 233); - y.setDuration(isBelowCenterY ? 233 : 500); + x.setDuration(isBelowCenterY ? APP_LAUNCH_DURATION : APP_LAUNCH_CURVED_DURATION); + y.setDuration(isBelowCenterY ? APP_LAUNCH_CURVED_DURATION : APP_LAUNCH_DURATION); x.setInterpolator(Interpolators.AGGRESSIVE_EASE); y.setInterpolator(Interpolators.AGGRESSIVE_EASE); appIconAnimatorSet.play(x); @@ -556,14 +615,10 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag float maxScaleX = mDeviceProfile.widthPx / (float) rect.width(); float maxScaleY = mDeviceProfile.heightPx / (float) rect.height(); float scale = Math.max(maxScaleX, maxScaleY); - ObjectAnimator sX = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_X, 1f, scale); - ObjectAnimator sY = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_Y, 1f, scale); - sX.setDuration(500); - sY.setDuration(500); - sX.setInterpolator(Interpolators.EXAGGERATED_EASE); - sY.setInterpolator(Interpolators.EXAGGERATED_EASE); - appIconAnimatorSet.play(sX); - appIconAnimatorSet.play(sY); + ObjectAnimator scaleAnim = ObjectAnimator + .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale); + scaleAnim.setDuration(APP_LAUNCH_DURATION).setInterpolator(Interpolators.EXAGGERATED_EASE); + appIconAnimatorSet.play(scaleAnim); // Fade out the app icon. ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f); @@ -572,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; } @@ -580,7 +643,11 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag */ private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) { Rect bounds = new Rect(); - if (v instanceof BubbleTextView) { + 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) { ((BubbleTextView) v).getIconBounds(bounds); } else { mDragLayer.getDescendantRectRelativeToSelf(v, bounds); @@ -591,7 +658,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag Matrix matrix = new Matrix(); ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); - appAnimator.setDuration(500); + appAnimator.setDuration(APP_LAUNCH_DURATION); appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { boolean isFirstFrame = true; @@ -631,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 @@ -678,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 */)); @@ -705,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.isInState(LauncherState.OVERVIEW)) - || !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; } }; } @@ -750,7 +802,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag Matrix matrix = new Matrix(); float height = mLauncher.getDeviceProfile().heightPx; float width = mLauncher.getDeviceProfile().widthPx; - float endX = (Utilities.isRtl(mLauncher.getResources()) ? -width : width) * 1.16f; + float endX = (mLauncher.getOverviewPanel().isRtl() ? -width : width) * 1.16f; ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1); closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS); @@ -809,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); @@ -842,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 aec28697e8..0000000000 --- a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java +++ /dev/null @@ -1,64 +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 AnimatorSet mAnimatorSet; - private Animator mLauncherAnimator; - private Animator mWindowAnimator; - - LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator) { - 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(); - } - - public boolean isRunning() { - return mAnimatorSet.isRunning(); - } - - public void finishLauncherAnimation() { - if (mLauncherAnimator != null) { - mLauncherAnimator.end(); - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java index 426fe35efb..2626e7cccc 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java @@ -24,7 +24,7 @@ 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; /** @@ -32,7 +32,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; */ public class AllAppsState extends LauncherState { - private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY; + 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 @@ -57,7 +57,8 @@ public class AllAppsState extends LauncherState { @Override public String getDescription(Launcher launcher) { - return launcher.getString(R.string.all_apps_button_label); + AllAppsContainerView appsView = launcher.getAppsView(); + return appsView.getDescription(); } @Override @@ -82,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 541c6bbc61..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java +++ /dev/null @@ -1,171 +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.anim.SpringAnimationHandler; -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.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 void initSprings() { - mSpringHandlers = new SpringAnimationHandler[0]; - } - - @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 new file mode 100644 index 0000000000..99bf2649b9 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.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.uioverrides; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.quickstep.QuickScrubController; +import com.android.quickstep.views.RecentsView; + +/** + * Extension of overview state used for QuickScrub + */ +public class FastOverviewState extends OverviewState { + + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_DISABLE_RESTORE + | FLAG_DISABLE_INTERACTION | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON; + + public FastOverviewState(int id) { + super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS); + } + + @Override + public void onStateTransitionEnd(Launcher launcher) { + super.onStateTransitionEnd(launcher); + RecentsView recentsView = launcher.getOverviewPanel(); + recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub(); + } + + public void onStateEnabled(Launcher launcher) { + super.onStateEnabled(launcher); + AbstractFloatingView.closeAllOpenViews(launcher); + } + + @Override + 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 9ba2308c62..a72b1b15bf 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -16,20 +16,18 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; +import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; -import android.content.Context; 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.R; -import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.quickstep.RecentsView; +import com.android.quickstep.views.RecentsView; /** * Definition for overview state @@ -37,27 +35,31 @@ import com.android.quickstep.RecentsView; public class OverviewState extends LauncherState { private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED - | FLAG_DISABLE_RESTORE; + | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI; public OverviewState(int id) { - super(id, ContainerType.TASKSWITCHER, OVERVIEW_TRANSITION_MS, STATE_FLAGS); + this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS); + } + + protected OverviewState(int id, int transitionDuration, int stateFlags) { + super(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags); } @Override public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { Rect pageRect = new Rect(); - RecentsView.getScaledDownPageRect(launcher.getDeviceProfile(), launcher, pageRect); - RecentsView rv = launcher.getOverviewPanel(); + RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, pageRect); if (launcher.getWorkspace().getNormalChildWidth() <= 0 || pageRect.isEmpty()) { return super.getWorkspaceScaleAndTranslation(launcher); } - float overlap = 0; - if (rv.getCurrentPage() >= rv.getFirstTaskIndex()) { - overlap = launcher.getResources().getDimension(R.dimen.workspace_overview_offset_x); - } - return getScaleAndTranslationForPageRect(launcher, overlap, pageRect); + return getScaleAndTranslationForPageRect(launcher, pageRect); + } + + @Override + public float[] getOverviewTranslationFactor(Launcher launcher) { + return new float[] {0f, 0f}; } @Override @@ -73,8 +75,8 @@ public class OverviewState extends LauncherState { } @Override - public float getVerticalProgress(Launcher launcher) { - return getVerticalProgress(launcher.getDeviceProfile(), launcher); + public void onStateTransitionEnd(Launcher launcher) { + launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE); } @Override @@ -83,51 +85,50 @@ public class OverviewState extends LauncherState { } public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { - final int centerPage = launcher.getWorkspace().getNextPage(); - return new PageAlphaProvider(ACCEL_2) { + return new PageAlphaProvider(DEACCEL_2) { @Override public float getPageAlpha(int pageIndex) { - return pageIndex != centerPage ? 0 : 1f; + return 0; } }; } - public static float[] getScaleAndTranslationForPageRect(Launcher launcher, float offsetX, - Rect pageRect) { + public static float[] getScaleAndTranslationForPageRect(Launcher launcher, Rect pageRect) { Workspace ws = launcher.getWorkspace(); float childWidth = ws.getNormalChildWidth(); - float childHeight = ws.getNormalChildHeight(); - float scale = pageRect.height() / childHeight; + float scale = pageRect.width() / childWidth; Rect insets = launcher.getDragLayer().getInsets(); float halfHeight = ws.getExpectedHeight() / 2; float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top); float translationY = pageRect.top - childTop; - // Align the workspace horizontally centered with the task rect - float halfWidth = ws.getExpectedWidth() / 2; - float childCenter = halfWidth - - scale * (halfWidth - ws.getPaddingLeft() - insets.left - childWidth / 2); - float translationX = pageRect.centerX() - childCenter; - - if (Utilities.isRtl(launcher.getResources())) { - translationX -= offsetX / scale; - } else { - translationX += offsetX / scale; - } - - return new float[] {scale, translationX, translationY}; + return new float[] {scale, 0, translationY}; } - public static float getVerticalProgress(DeviceProfile grid, Context context) { - if (!grid.isVerticalBarLayout()) { - return 1f; + @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; } + } - float total = grid.heightPx; - float searchHeight = total - grid.availableHeightPx + - context.getResources().getDimension(R.dimen.all_apps_search_box_full_height); - return 1 - (searchHeight / total); + @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 7d371e96bf..d8f206c5ed 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -16,147 +16,84 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; +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.Utilities; -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.RecentsView; -import com.android.quickstep.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 WorkspaceCard mWorkspaceCard; - - 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 mIsRecentsScrollingToFirstTask; + private final LauncherRecentsView mRecentsView; public RecentsViewStateController(Launcher launcher) { mLauncher = launcher; mRecentsView = launcher.getOverviewPanel(); - mRecentsView.setStateController(this); - - mWorkspaceCard = (WorkspaceCard) mRecentsView.getChildAt(0); - mWorkspaceCard.setup(launcher); } @Override public void setState(LauncherState state) { - mWorkspaceCard.setWorkspaceScrollingEnabled(state == OVERVIEW); - setVisibility(state == OVERVIEW); - setTransitionProgress(state == OVERVIEW ? 1 : 0); - if (state == OVERVIEW) { - for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) { - ((TaskView) mRecentsView.getPageAt(i)).resetVisualProperties(); - } - mRecentsView.updateCurveProperties(); + 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(); } } @Override public void setStateWithAnimation(final LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { - boolean settingEnabled = Utilities.getPrefs(mLauncher) - .getBoolean("pref_scroll_to_first_task", false); - mIsRecentsScrollingToFirstTask = mLauncher.isInState(NORMAL) && toState == OVERVIEW - && settingEnabled; - // TODO: Instead of animating the workspace translationX, move the contents - mWorkspaceCard.setWorkspaceScrollingEnabled(mIsRecentsScrollingToFirstTask); // Scroll to the workspace card before changing to the NORMAL state. + LauncherState fromState = mLauncher.getStateManager().getState(); int currPage = mRecentsView.getCurrentPage(); - if (toState == NORMAL && currPage != 0 && !config.userControlled) { + if (fromState.overviewUi && toState == NORMAL && currPage != 0 && !config.userControlled) { int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION; int durationPerPage = maxSnapDuration / 10; int snapDuration = Math.min(maxSnapDuration, durationPerPage * currPage); mRecentsView.snapToPage(0, snapDuration); - builder.setStartDelay(snapDuration); + // Let the snapping animation play for a bit before we translate off screen. + builder.setStartDelay(snapDuration / 4); } - ObjectAnimator progressAnim = - mTransitionProgress.animateToValue(toState == OVERVIEW ? 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) { - mWorkspaceCard.setWorkspaceScrollingEnabled(toState == OVERVIEW); - mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen()); - } - }); - builder.play(progressAnim); - - ObjectAnimator visibilityAnim = animateVisibility(toState == OVERVIEW); - 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); - } - }); - } else { - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mRecentsView.setVisibility(View.GONE); - } + 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(); }); + updateAnim.setDuration(config.duration); + builder.play(updateAnim); } - return anim; - } - - public void setTransitionProgress(float progress) { - mTransitionProgress.cancelAnimation(); - mTransitionProgress.updateValue(progress); - } - - private void onTransitionProgress() { - applyProgress(); - if (mIsRecentsScrollingToFirstTask) { - int scrollForFirstTask = mRecentsView.getScrollForPage(mRecentsView.getFirstTaskIndex()); - int scrollForPage0 = mRecentsView.getScrollForPage(0); - mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask - + (1 - mTransitionProgress.value) * scrollForPage0)); - } - } - - private void onVisibilityProgress() { - applyProgress(); - } - - private void applyProgress() { - mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value); } } 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 62% rename from quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java rename to quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index 468a5617cb..d11547de84 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -15,10 +15,12 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.util.Log; import android.view.MotionEvent; @@ -26,30 +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.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.TouchController; -import com.android.quickstep.RecentsView; -import com.android.quickstep.TaskView; - -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.DEACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +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"; @@ -65,20 +58,19 @@ public class OverviewSwipeController extends AnimatorListenerAdapter private final RecentsView mRecentsView; private final int[] mTempCords = new int[2]; + private PendingAnimation mPendingAnimation; private AnimatorPlaybackController mCurrentAnimation; 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); @@ -95,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()) { @@ -130,30 +113,15 @@ public class OverviewSwipeController extends AnimatorListenerAdapter ignoreSlopWhenSettling = true; } else { mTaskBeingDragged = null; - mSwipeDownEnabled = true; - int currentPage = mRecentsView.getCurrentPage(); - if (currentPage == 0) { - // User is on home tile + 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; } else { - View view = mRecentsView.getChildAt(currentPage); - if (mLauncher.getDragLayer().isEventOverView(view, ev) && - view instanceof TaskView) { - // 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; - } + mNoIntercept = true; + return false; } } @@ -175,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; @@ -185,44 +150,30 @@ public class OverviewSwipeController extends AnimatorListenerAdapter if (mCurrentAnimation != null) { mCurrentAnimation.setPlayFraction(0); } + if (mPendingAnimation != null) { + mPendingAnimation.finish(false); + mPendingAnimation = null; + } + mCurrentAnimationIsGoingUp = goingUp; float range = mLauncher.getAllAppsController().getShiftRange(); 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) { - AnimatorSet anim = new AnimatorSet(); - ObjectAnimator translate = ObjectAnimator.ofFloat( - mTaskBeingDragged, View.TRANSLATION_Y, -mTaskBeingDragged.getBottom()); - translate.setInterpolator(LINEAR); - translate.setDuration(maxDuration); - anim.play(translate); + AnimatorSet anim = new AnimatorSet(); + // TODO: Setup a zoom animation + mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); - ObjectAnimator alpha = ObjectAnimator.ofFloat(mTaskBeingDragged, View.ALPHA, 0); - alpha.setInterpolator(DEACCEL_1_5); - alpha.setDuration(maxDuration); - anim.play(alpha); - mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); - mEndDisplacement = -mTaskBeingDragged.getBottom(); - } else { - 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); @@ -260,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() @@ -288,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(); @@ -299,27 +247,17 @@ public class OverviewSwipeController extends AnimatorListenerAdapter } private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) { - if (mTaskBeingDragged == null) { - LauncherState state = wasSuccess ? - (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW; - mLauncher.getStateManager().goToState(state, false); - - } else if (wasSuccess) { - if (mCurrentAnimationIsGoingUp) { - mRecentsView.onTaskDismissed(mTaskBeingDragged); - } else { + if (mPendingAnimation != null) { + mPendingAnimation.finish(wasSuccess); + mPendingAnimation = null; + } + 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 2695054c23..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java +++ /dev/null @@ -1,496 +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.launcher3.anim.SpringAnimationHandler.Y_DIRECTION; -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.support.animation.SpringAnimation; -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.allapps.AllAppsContainerView; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.anim.SpringAnimationHandler; -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; - -import java.util.ArrayList; - -/** - * 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; - - private SpringAnimationHandler[] mSpringHandlers; - - 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(); - } - } - - private void initSprings() { - AllAppsContainerView appsView = mLauncher.getAppsView(); - - SpringAnimationHandler handler = appsView.getSpringAnimationHandler(); - if (handler == null) { - mSpringHandlers = new SpringAnimationHandler[0]; - return; - } - - ArrayList handlers = new ArrayList<>(); - handlers.add(handler); - - SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling(); - if (searchSpring != null) { - SpringAnimationHandler searchHandler = - new SpringAnimationHandler(Y_DIRECTION, handler.getFactory()); - searchHandler.add(searchSpring, true /* setDefaultValues */); - handlers.add(searchHandler); - } - - mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]); - } - - @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 (mSpringHandlers == null) { - initSprings(); - } - } - - if (mNoIntercept) { - return false; - } - - onControllerTouchEvent(ev); - return mDetector.isDraggingOrSettling(); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - for (SpringAnimationHandler h : mSpringHandlers) { - h.addMovement(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(); - } - - for (SpringAnimationHandler h : mSpringHandlers) { - h.skipToEnd(); - } - } - - 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; - } - - if (fling && targetState == ALL_APPS) { - for (SpringAnimationHandler h : mSpringHandlers) { - // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) - h.animateToFinalPosition(0 /* pos */, 1 /* startValue */); - } - } - - 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 7bd43668ec..846e80377e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -16,41 +16,46 @@ 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.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.PointF; +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.graphics.BitmapRenderer; +import com.android.launcher3.R; import com.android.launcher3.util.TouchController; import com.android.quickstep.OverviewInteractionState; -import com.android.quickstep.RecentsView; -import com.android.systemui.shared.recents.view.RecentsTransition; +import com.android.quickstep.RecentsModel; +import com.android.quickstep.views.RecentsView; public class UiFactory { - private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = - "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; - - public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD; - 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 IgnoreTouchesInQuickScrub(), - 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 IgnoreTouchesInQuickScrub(), - new TwoStepSwipeController(launcher), - new OverviewSwipeController(launcher)}; + launcher.getDragController(), + new PortraitStatesTouchController(launcher), + new TaskViewTouchController(launcher)}; } } @@ -64,28 +69,41 @@ public class UiFactory { new RecentsViewStateController(launcher)}; } - public static void onWorkspaceLongPress(Launcher launcher, PointF touchPoint) { - OptionsPopupView.show(launcher, touchPoint.x, touchPoint.y); - } - public static void onLauncherStateOrFocusChanged(Launcher launcher) { - OverviewInteractionState.setBackButtonVisible(launcher, launcher == null - || !launcher.isInState(NORMAL) || !launcher.hasWindowFocus()); - } - - public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer, - BitmapRenderer renderer) { - if (USE_HARDWARE_BITMAP && !forceSoftwareRenderer) { - return RecentsTransition.createHardwareBitmap(width, height, renderer::render); - } else { - Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - renderer.render(new Canvas(result)); - return result; + boolean shouldBackButtonBeHidden = launcher != null + && launcher.getStateManager().getState().hideBackButton + && launcher.hasWindowFocus(); + if (shouldBackButtonBeHidden) { + // Show the back button if there is a floating view visible. + shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenView(launcher) == null; } + OverviewInteractionState.getInstance(launcher) + .setBackButtonVisible(!shouldBackButtonBeHidden); } public static void resetOverview(Launcher launcher) { RecentsView recents = launcher.getOverviewPanel(); recents.reset(); } + + public static void onStart(Context context) { + RecentsModel model = RecentsModel.getInstance(context); + if (model != null) { + model.onStart(); + } + } + + 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/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java deleted file mode 100644 index 8533502b06..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java +++ /dev/null @@ -1,127 +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.NORMAL; -import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; - -import com.android.launcher3.Launcher; -import com.android.launcher3.R; -import com.android.launcher3.Workspace; -import com.android.quickstep.RecentsView; -import com.android.quickstep.RecentsView.PageCallbacks; -import com.android.quickstep.RecentsView.ScrollState; - -public class WorkspaceCard extends View implements PageCallbacks, OnClickListener { - - private final Rect mTempRect = new Rect(); - - private Launcher mLauncher; - private Workspace mWorkspace; - - private float mLinearInterpolationForPage2 = 1; - private float mTranslateXPage0, mTranslateXPage1; - private float mExtraScrollShift; - - private boolean mIsWorkspaceScrollingEnabled; - - public WorkspaceCard(Context context) { - this(context, null); - } - - public WorkspaceCard(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WorkspaceCard(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - setOnClickListener(this); - } - - /** - * Draw nothing. - */ - @Override - public void draw(Canvas canvas) { } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - // Initiate data - mLinearInterpolationForPage2 = RecentsView.getScaledDownPageRect( - mLauncher.getDeviceProfile(), mLauncher, mTempRect); - - float[] scale = OverviewState.getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect); - mTranslateXPage0 = scale[1]; - mTranslateXPage1 = OverviewState - .getScaleAndTranslationForPageRect(mLauncher, - getResources().getDimension(R.dimen.workspace_overview_offset_x) / scale[0], - mTempRect)[1]; - - mExtraScrollShift = 0; - if (mWorkspace != null && getWidth() > 0) { - float workspaceWidth = mWorkspace.getNormalChildWidth() * scale[0]; - mExtraScrollShift = (workspaceWidth - getWidth()) / 2; - setScaleX(workspaceWidth / getWidth()); - } - } - - @Override - public void onClick(View view) { - mLauncher.getStateManager().goToState(NORMAL); - } - - public void setup(Launcher launcher) { - mLauncher = launcher; - mWorkspace = mLauncher.getWorkspace(); - } - - public void setWorkspaceScrollingEnabled(boolean isEnabled) { - mIsWorkspaceScrollingEnabled = isEnabled; - } - - @Override - public int onPageScroll(ScrollState scrollState) { - float factor = scrollState.linearInterpolation; - float translateX = scrollState.distanceFromScreenCenter; - if (mIsWorkspaceScrollingEnabled) { - float shift = factor * (mTranslateXPage1 - mTranslateXPage0); - mWorkspace.setTranslationX(shift + mTranslateXPage0); - translateX += shift; - } - - setTranslationX(translateX); - - // If the workspace card is still the first page, shift all the other pages. - if (scrollState.linearInterpolation > mLinearInterpolationForPage2) { - scrollState.prevPageExtraWidth = 0; - } else if (mLinearInterpolationForPage2 > 0) { - scrollState.prevPageExtraWidth = mExtraScrollShift * - (1 - scrollState.linearInterpolation / mLinearInterpolationForPage2); - } else { - scrollState.prevPageExtraWidth = mExtraScrollShift; - } - return SCROLL_TYPE_WORKSPACE; - } -} 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/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java index 214b3f3b9f..84dfa457e9 100644 --- a/quickstep/src/com/android/quickstep/AnimatedFloat.java +++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java @@ -77,6 +77,12 @@ public class AnimatedFloat { } } + public void finishAnimation() { + if (mValueAnimator != null && mValueAnimator.isRunning()) { + mValueAnimator.end(); + } + } + public ObjectAnimator getCurrentAnimation() { return mValueAnimator; } diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java deleted file mode 100644 index b3ebd772cc..0000000000 --- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java +++ /dev/null @@ -1,49 +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; - - 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/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java new file mode 100644 index 0000000000..b92678a2ef --- /dev/null +++ b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java @@ -0,0 +1,99 @@ +/* + * 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 android.view.Choreographer; +import android.view.MotionEvent; +import android.view.VelocityTracker; + +/** + * A TouchConsumer which defers all events on the UIThread until the consumer is created. + */ +@TargetApi(Build.VERSION_CODES.P) +public class DeferredTouchConsumer implements TouchConsumer { + + private final VelocityTracker mVelocityTracker; + private final DeferredTouchProvider mTouchProvider; + + private MotionEventQueue mMyQueue; + private TouchConsumer mTarget; + + public DeferredTouchConsumer(DeferredTouchProvider touchProvider) { + mVelocityTracker = VelocityTracker.obtain(); + mTouchProvider = touchProvider; + } + + @Override + public void accept(MotionEvent event) { + mTarget.accept(event); + } + + @Override + public void reset() { + mTarget.reset(); + } + + @Override + public void updateTouchTracking(int interactionType) { + mTarget.updateTouchTracking(interactionType); + } + + @Override + public void onQuickScrubEnd() { + mTarget.onQuickScrubEnd(); + } + + @Override + public void onQuickScrubProgress(float progress) { + mTarget.onQuickScrubProgress(progress); + } + + @Override + public void preProcessMotionEvent(MotionEvent ev) { + mVelocityTracker.addMovement(ev); + } + + @Override + public Choreographer getIntrimChoreographer(MotionEventQueue queue) { + mMyQueue = queue; + return null; + } + + @Override + public void deferInit() { + mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker); + mTarget.getIntrimChoreographer(mMyQueue); + } + + @Override + public boolean forceToLauncherConsumer() { + return mTarget.forceToLauncherConsumer(); + } + + @Override + public boolean deferNextEventToMainThread() { + // If our target is still null, defer the next target as well + TouchConsumer target = mTarget; + return target == null ? true : target.deferNextEventToMainThread(); + } + + public interface DeferredTouchProvider { + + TouchConsumer createTouchConsumer(VelocityTracker tracker); + } +} 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 new file mode 100644 index 0000000000..032d753a68 --- /dev/null +++ b/quickstep/src/com/android/quickstep/FallbackRecentsView.java @@ -0,0 +1,68 @@ +/* + * 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.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Insettable; +import com.android.quickstep.views.RecentsView; + +public class FallbackRecentsView extends RecentsView implements Insettable { + + public FallbackRecentsView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOverviewStateEnabled(true); + } + + @Override + protected void onAllTasksRemoved() { + mActivity.finish(); + } + + @Override + public void setInsets(Rect insets) { + mInsets.set(insets); + DeviceProfile dp = mActivity.getDeviceProfile(); + Rect padding = getPadding(dp, getContext()); + verticalCenter(padding, dp); + setPadding(padding.left, padding.top, padding.right, padding.bottom); + } + + 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/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java new file mode 100644 index 0000000000..12757c0f79 --- /dev/null +++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java @@ -0,0 +1,77 @@ +/* + * 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.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.InstantAppInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +import com.android.launcher3.AppInfo; +import com.android.launcher3.util.InstantAppResolver; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of InstantAppResolver using platform APIs + */ +@SuppressWarnings("unused") +public class InstantAppResolverImpl extends InstantAppResolver { + + private static final String TAG = "InstantAppResolverImpl"; + public static final String COMPONENT_CLASS_MARKER = "@instantapp"; + + private final PackageManager mPM; + + public InstantAppResolverImpl(Context context) + throws NoSuchMethodException, ClassNotFoundException { + mPM = context.getPackageManager(); + } + + @Override + public boolean isInstantApp(ApplicationInfo info) { + return info.isInstantApp(); + } + + @Override + public boolean isInstantApp(AppInfo info) { + ComponentName cn = info.getTargetComponent(); + return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER); + } + + @Override + public List getInstantApps() { + try { + List result = new ArrayList<>(); + for (InstantAppInfo iai : mPM.getInstantApps()) { + ApplicationInfo info = iai.getApplicationInfo(); + if (info != null) { + result.add(info); + } + } + return result; + } catch (SecurityException se) { + Log.w(TAG, "getInstantApps failed. Launcher may not be the default home app.", se); + } catch (Exception e) { + Log.e(TAG, "Error calling API: getInstantApps", e); + } + return super.getInstantApps(); + } +} 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 6e92d83c52..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,16 +41,18 @@ 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 | (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(); private final Object mExecutionLock = new Object(); @@ -76,10 +76,10 @@ public class MotionEventQueue { public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) { mMainChoreographer = choreographer; mConsumer = consumer; - mCurrentChoreographer = mMainChoreographer; mCurrentRunnable = mMainFrameCallback; - setInterimChoreographerLocked(consumer.getIntrimChoreographer(this)); + + setInterimChoreographer(consumer.getIntrimChoreographer(this)); } public void setInterimChoreographer(Choreographer choreographer) { @@ -141,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; @@ -156,6 +153,13 @@ public class MotionEventQueue { case ACTION_RESET: mConsumer.reset(); break; + 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()); } @@ -184,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); } @@ -204,6 +208,14 @@ public class MotionEventQueue { queueVirtualAction(ACTION_RESET, 0); } + public void deferInit() { + queueVirtualAction(ACTION_DEFER_INIT, 0); + } + + public TouchConsumer getConsumer() { + return mConsumer; + } + private static class EventArray extends ArrayList { public int lastEventAction = ACTION_CANCEL; diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java deleted file mode 100644 index ff7d434e7f..0000000000 --- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java +++ /dev/null @@ -1,392 +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_SCRUB; -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.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) { - ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration); - anim.setInterpolator(Interpolators.SCROLL); - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0) - ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS); - } - }); - 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/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java index 431fb30a12..f875bb786e 100644 --- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java +++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java @@ -16,19 +16,20 @@ package com.android.quickstep; import android.annotation.TargetApi; +import android.app.ActivityManager.TaskDescription; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Build.VERSION_CODES; import android.os.UserHandle; import android.util.LruCache; import android.util.SparseArray; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.graphics.BitmapInfo; +import com.android.launcher3.graphics.DrawableFactory; import com.android.launcher3.graphics.LauncherIcons; import com.android.systemui.shared.recents.model.IconLoader; import com.android.systemui.shared.recents.model.TaskKeyLruCache; @@ -40,11 +41,13 @@ import com.android.systemui.shared.recents.model.TaskKeyLruCache; public class NormalizedIconLoader extends IconLoader { private final SparseArray mDefaultIcons = new SparseArray<>(); + private final DrawableFactory mDrawableFactory; private LauncherIcons mLauncherIcons; public NormalizedIconLoader(Context context, TaskKeyLruCache iconCache, LruCache activityInfoCache) { super(context, iconCache, activityInfoCache); + mDrawableFactory = DrawableFactory.get(context); } @Override @@ -53,7 +56,7 @@ public class NormalizedIconLoader extends IconLoader { BitmapInfo info = mDefaultIcons.get(userId); if (info == null) { info = getBitmapInfo(Resources.getSystem() - .getDrawable(android.R.drawable.sym_def_app_icon), userId); + .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false); mDefaultIcons.put(userId, info); } @@ -62,23 +65,31 @@ public class NormalizedIconLoader extends IconLoader { } @Override - protected Drawable createBadgedDrawable(Drawable drawable, int userId) { - return new FastBitmapDrawable(getBitmapInfo(drawable, userId)); + protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) { + return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(), + false)); } - private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId) { + private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId, + int primaryColor, boolean isInstantApp) { if (mLauncherIcons == null) { mLauncherIcons = LauncherIcons.obtain(mContext); } + mLauncherIcons.setWrapperBackgroundColor(primaryColor); // User version code O, so that the icon is always wrapped in an adaptive icon container. return mLauncherIcons.createBadgedIconBitmap(drawable, UserHandle.of(userId), - Build.VERSION_CODES.O); + Build.VERSION_CODES.O, isInstantApp); } @Override - protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId) { - return createBadgedDrawable( - activityInfo.loadUnbadgedIcon(mContext.getPackageManager()), userId); + protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId, + TaskDescription desc) { + BitmapInfo bitmapInfo = getBitmapInfo( + activityInfo.loadUnbadgedIcon(mContext.getPackageManager()), + userId, + desc.getPrimaryColor(), + activityInfo.applicationInfo.isInstantApp()); + return mDrawableFactory.newIcon(bitmapInfo, activityInfo); } } diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 488cd72b53..4877abbe19 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -21,27 +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.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; -import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; +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.metrics.LogMaker; +import android.os.Build; import android.os.Bundle; import android.os.Looper; import android.os.SystemClock; -import android.util.Log; import android.view.Choreographer; import android.view.Display; import android.view.MotionEvent; @@ -51,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; @@ -63,87 +54,60 @@ import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.WindowManagerWrapper; +import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -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()); - } -} - /** * Touch consumer for handling events originating from an activity other than Launcher */ +@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 = 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; + private final boolean mIsDeferredDownTarget; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); private int mActivePointerId = INVALID_POINTER_ID; private boolean mTouchThresholdCrossed; private int mTouchSlop; private float mStartDisplacement; - private BaseSwipeInteractionHandler mInteractionHandler; + private WindowTransformSwipeHandler mInteractionHandler; private int mDisplayRotation; private Rect mStableInsets = new Rect(); - private @HitTarget int mDownHitTarget = HIT_TARGET_NONE; private VelocityTracker mVelocityTracker; private MotionEventQueue mEventQueue; - - private final MetricsLogger mMetricsLogger = new MetricsLogger(); + 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) { + @HitTarget int downHitTarget, VelocityTracker velocityTracker) { super(base); mRunningTask = runningTaskInfo; mRecentsModel = recentsModel; mHomeIntent = homeIntent; - mVelocityTracker = VelocityTracker.obtain(); - mISystemUiProxy = systemUiProxy; + mVelocityTracker = velocityTracker; + mActivityControlHelper = activityControl; mMainThreadExecutor = mainThreadExecutor; mBackgroundThreadChoreographer = backgroundThreadChoreographer; - mDownHitTarget = downHitTarget; + mIsDeferredDownTarget = Arrays.binarySearch(DEFERRED_HIT_TARGETS, downHitTarget) >= 0; + } + + @Override + public void onShowOverviewFromAltTab() { + startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis()); } @Override @@ -162,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() && mDownHitTarget != HIT_TARGET_BACK) { + if (!mIsDeferredDownTarget) { startTouchTrackingForWindowAnimation(ev.getEventTime()); } @@ -202,14 +166,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC if (mTouchThresholdCrossed) { mStartDisplacement = Math.signum(displacement) * mTouchSlop; - if (isUsingScreenShot()) { - startTouchTrackingForScreenshotAnimation(); - } else if (mDownHitTarget == HIT_TARGET_BACK) { + if (mIsDeferredDownTarget) { // If we deferred starting the window animation on touch down, then // start tracking now startTouchTrackingForWindowAnimation(ev.getEventTime()); } - notifyGestureStarted(); } } else if (mInteractionHandler != null) { @@ -235,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() { @@ -250,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); + final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler( + mRunningTask, this, touchTimeMs, mActivityControlHelper); // Preload the plan mRecentsModel.loadTasks(mRunningTask.id, null); @@ -329,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) { @@ -350,16 +246,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC TraceHelper.endSection("RecentsController", "Finishing no handler"); controller.finish(false /* toHome */); } - - // 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*/, - SystemClock.uptimeMillis() - touchTimeMs); - mMetricsLogger.write(builder); } public void onAnimationCanceled() { @@ -369,7 +255,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC handler.onRecentsAnimationCanceled(); } } - }, null, null); + }); if (Looper.myLooper() != Looper.getMainLooper()) { startActivity.run(); @@ -398,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(); @@ -415,8 +301,9 @@ 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); } } @@ -424,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 @@ -471,4 +349,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } } } + + @Override + public boolean forceToLauncherConsumer() { + return mIsGoingToHome; + } + + @Override + public boolean deferNextEventToMainThread() { + // TODO: Consider also check if the eventQueue is using mainThread of not. + return mInteractionHandler != null; + } } diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java new file mode 100644 index 0000000000..311411f4cf --- /dev/null +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -0,0 +1,369 @@ +/* + * 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.OVERVIEW; + +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. + */ +@TargetApi(Build.VERSION_CODES.P) +public class OverviewCommandHelper extends InternalStateHandler { + + 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); + + 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); + } + + private void openRecents() { + Intent intent = addToIntent(new Intent(homeIntent)); + mContext.startActivity(intent); + initWhenReady(); + } + + @UiThread + private void addFinishCommand(int requestId, int id, Runnable action) { + if (requestId < mCurrentCommandId) { + action.run(); + } else { + mCurrentCommandFinishRunnables.put(id, action); + } + } + + @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); + } + } + + private boolean isOverviewAlmostVisible() { + if (clearReference()) { + return true; + } + if (!mAM.getRunningTask().topActivity.equals(launcher)) { + return false; + } + Launcher launcher = getLauncher(); + return launcher != null && launcher.isStarted() && launcher.isInState(OVERVIEW); + } + + private Launcher getLauncher() { + return (Launcher) LauncherAppState.getInstance(mContext).getModel().getCallback(); + } + + @Override + protected boolean init(Launcher launcher, boolean alreadyOnHome) { + AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome); + launcher.getStateManager().goToState(OVERVIEW, alreadyOnHome); + clearReference(); + 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 3c68281a82..22b175779d 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -15,18 +15,28 @@ */ package com.android.quickstep; +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: * @@ -37,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; - - 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/PendingAnimation.java b/quickstep/src/com/android/quickstep/PendingAnimation.java new file mode 100644 index 0000000000..d22ef61053 --- /dev/null +++ b/quickstep/src/com/android/quickstep/PendingAnimation.java @@ -0,0 +1,53 @@ +/* + * 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.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.os.Build; + +import java.util.ArrayList; +import java.util.function.Consumer; + +/** + * Utility class to keep track of a running animation. + * + * This class allows attaching end callbacks to an animation is intended to be used with + * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case + * AnimationListeners are not properly dispatched. + */ +@TargetApi(Build.VERSION_CODES.O) +public class PendingAnimation { + + private final ArrayList> mEndListeners = new ArrayList<>(); + + public final AnimatorSet anim; + + public PendingAnimation(AnimatorSet anim) { + this.anim = anim; + } + + public void finish(boolean isSuccess) { + for (Consumer listeners : mEndListeners) { + listeners.accept(isSuccess); + } + mEndListeners.clear(); + } + + public void addEndListener(Consumer listener) { + mEndListeners.add(listener); + } +} diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java index f28d51c8ea..d868d122b7 100644 --- a/quickstep/src/com/android/quickstep/QuickScrubController.java +++ b/quickstep/src/com/android/quickstep/QuickScrubController.java @@ -19,13 +19,14 @@ package com.android.quickstep; import android.view.HapticFeedbackConstants; import com.android.launcher3.Alarm; -import com.android.launcher3.Launcher; +import com.android.launcher3.BaseActivity; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.Utilities; -import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; /** * Responds to quick scrub callbacks to page through and launch recent tasks. @@ -35,8 +36,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; */ public class QuickScrubController implements OnAlarmListener { - public static final int QUICK_SWITCH_START_DURATION = 133; - public static final int QUICK_SWITCH_SNAP_DURATION = 120; + 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; @@ -47,15 +47,16 @@ public class QuickScrubController implements OnAlarmListener { private final Alarm mAutoAdvanceAlarm; private final RecentsView mRecentsView; - private final Launcher mLauncher; + private final BaseActivity mActivity; private boolean mInQuickScrub; private int mQuickScrubSection; - private int mStartPage; + private boolean mStartedFromHome; private boolean mHasAlarmRun; + private boolean mFinishedTransitionToQuickScrub; - public QuickScrubController(Launcher launcher, RecentsView recentsView) { - mLauncher = launcher; + public QuickScrubController(BaseActivity activity, RecentsView recentsView) { + mActivity = activity; mRecentsView = recentsView; if (ENABLE_AUTO_ADVANCE) { mAutoAdvanceAlarm = new Alarm(); @@ -65,10 +66,13 @@ public class QuickScrubController implements OnAlarmListener { public void onQuickScrubStart(boolean startingFromHome) { mInQuickScrub = true; - mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex(); + mStartedFromHome = startingFromHome; mQuickScrubSection = 0; mHasAlarmRun = false; - mLauncher.getUserEventDispatcher().resetActionDurationMillis(); + mFinishedTransitionToQuickScrub = false; + + snapToNextTaskIfAvailable(); + mActivity.getUserEventDispatcher().resetActionDurationMillis(); } public void onQuickScrubEnd() { @@ -78,10 +82,12 @@ public class QuickScrubController implements OnAlarmListener { } int page = mRecentsView.getNextPage(); Runnable launchTaskRunnable = () -> { - if (page < mRecentsView.getFirstTaskIndex()) { - mRecentsView.getPageAt(page).performClick(); + TaskView taskView = ((TaskView) mRecentsView.getPageAt(page)); + if (taskView != null) { + taskView.launchTask(true); } else { - ((TaskView) mRecentsView.getPageAt(page)).launchTask(true); + // Break out of quick scrub so user can interact with launcher. + mActivity.onBackPressed(); } }; int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen()) @@ -93,8 +99,8 @@ public class QuickScrubController implements OnAlarmListener { // No page move needed, just launch it launchTaskRunnable.run(); } - mLauncher.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP, - ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ? + mActivity.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP, + ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ? ContainerType.WORKSPACE : ContainerType.APP); } @@ -102,7 +108,9 @@ public class QuickScrubController implements OnAlarmListener { int quickScrubSection = Math.round(progress * NUM_QUICK_SCRUB_SECTIONS); if (quickScrubSection != mQuickScrubSection) { int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection; - goToPageWithHaptic(pageToGoTo); + if (mFinishedTransitionToQuickScrub) { + goToPageWithHaptic(pageToGoTo); + } if (ENABLE_AUTO_ADVANCE) { if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) { mAutoAdvanceAlarm.setAlarm(mHasAlarmRun @@ -115,39 +123,24 @@ public class QuickScrubController implements OnAlarmListener { } } - public void onQuickSwitch() { - for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) { - TaskView taskView = (TaskView) mRecentsView.getPageAt(i); - if (taskView.getTask().key.id != mRecentsView.getRunningTaskId()) { - Runnable launchTaskRunnable = () -> taskView.launchTask(true); - if (mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION)) { - // Snap to the new page then launch it - mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable); - } else { - // No need to move page, just launch task directly - launchTaskRunnable.run(); - } - break; - } - } - mLauncher.getUserEventDispatcher().logActionOnControl(Touch.FLING, - ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ? - ContainerType.WORKSPACE : ContainerType.APP); + public void onFinishedTransitionToQuickScrub() { + mFinishedTransitionToQuickScrub = true; } - public void snapToPageForCurrentQuickScrubSection() { - if (mInQuickScrub) { - goToPageWithHaptic(mRecentsView.getFirstTaskIndex() + mQuickScrubSection); + public void snapToNextTaskIfAvailable() { + if (mInQuickScrub && mRecentsView.getChildCount() > 0) { + int toPage = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1; + mRecentsView.snapToPage(toPage, QUICK_SCRUB_START_DURATION); } } private void goToPageWithHaptic(int pageToGoTo) { - pageToGoTo = Utilities.boundToRange(pageToGoTo, mStartPage, mRecentsView.getPageCount() - 1); + pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1); if (pageToGoTo != mRecentsView.getNextPage()) { int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage()) - * QUICKSCRUB_SNAP_DURATION_PER_PAGE; + * QUICKSCRUB_SNAP_DURATION_PER_PAGE; mRecentsView.snapToPage(pageToGoTo, duration); - mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, + mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } } @@ -158,7 +151,7 @@ public class QuickScrubController implements OnAlarmListener { if (mQuickScrubSection == NUM_QUICK_SCRUB_SECTIONS && currPage < mRecentsView.getPageCount() - 1) { goToPageWithHaptic(currPage + 1); - } else if (mQuickScrubSection == 0 && currPage > mStartPage) { + } else if (mQuickScrubSection == 0 && currPage > 0) { goToPageWithHaptic(currPage - 1); } mHasAlarmRun = true; diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index f92d773bd6..e5792056aa 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,34 +15,88 @@ */ package com.android.quickstep; -import android.app.ListActivity; +import android.app.ActivityOptions; import android.os.Bundle; -import android.os.UserHandle; -import android.support.annotation.Nullable; -import android.widget.ArrayAdapter; +import android.view.View; -import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; -import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions; -import com.android.systemui.shared.recents.model.RecentsTaskLoader; -import com.android.systemui.shared.recents.model.Task; +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 ListActivity { +public class RecentsActivity extends BaseDraggingActivity { - private ArrayAdapter mAdapter; + private RecentsRootView mRecentsRootView; + private FallbackRecentsView mFallbackRecentsView; @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(this); - plan.preloadPlan(new PreloadOptions(), new RecentsTaskLoader(this, 1, 1, 0), -1, - UserHandle.myUserId()); + // In case we are reusing IDP, create a copy so that we dont conflict with Launcher + // activity. + LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); + setDeviceProfile(appState != null + ? appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this) + : new InvariantDeviceProfile(this).getDeviceProfile(this)); - mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); - mAdapter.addAll(plan.getTaskStack().getTasks()); - setListAdapter(mAdapter); + 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 7fe77510df..1e43202a1f 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -16,6 +16,8 @@ package com.android.quickstep; import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -28,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; @@ -51,7 +54,6 @@ import java.util.function.Consumer; */ @TargetApi(Build.VERSION_CODES.O) public class RecentsModel extends TaskStackChangeListener { - // We do not need any synchronization for this variable as its only written on UI thread. private static RecentsModel INSTANCE; @@ -83,10 +85,17 @@ public class RecentsModel extends TaskStackChangeListener { private int mTaskChangeId; private ISystemUiProxy mSystemUiProxy; private boolean mClearAssistCacheOnStackChange = true; + private final boolean mPreloadTasksInBackground; + private final AccessibilityManager mAccessibilityManager; private RecentsModel(Context context) { mContext = context; + ActivityManager activityManager = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mPreloadTasksInBackground = !activityManager.isLowRamDevice(); + mMainThreadExecutor = new MainThreadExecutor(); + Resources res = context.getResources(); mRecentsTaskLoader = new RecentsTaskLoader(mContext, res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize), @@ -100,12 +109,11 @@ public class RecentsModel extends TaskStackChangeListener { } }; mRecentsTaskLoader.startLoader(mContext); - - mMainThreadExecutor = new MainThreadExecutor(); ActivityManagerWrapper.getInstance().registerTaskStackListener(this); mTaskChangeId = 1; loadTasks(-1, null); + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } public RecentsTaskLoader getRecentsTaskLoader() { @@ -135,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(() -> { @@ -162,6 +170,31 @@ public class RecentsModel extends TaskStackChangeListener { } } + @Override + public void onTaskStackChangedBackground() { + int userId = UserHandle.myUserId(); + if (!mPreloadTasksInBackground || !checkCurrentUserId(userId, false /* debug */)) { + // TODO: Only register this for the current user + return; + } + + // Preload a fixed number of task icons/thumbnails in the background + ActivityManager.RunningTaskInfo runningTaskInfo = + ActivityManagerWrapper.getInstance().getRunningTask(); + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); + RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); + launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1; + launchOpts.numVisibleTasks = 2; + launchOpts.numVisibleTaskThumbnails = 2; + launchOpts.onlyLoadForCache = true; + launchOpts.onlyLoadPausedActivities = true; + launchOpts.loadThumbnails = true; + PreloadOptions preloadOpts = new PreloadOptions(); + preloadOpts.loadTitles = mAccessibilityManager.isEnabled(); + plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId); + mRecentsTaskLoader.loadTasks(plan, launchOpts); + } + public boolean isLoadPlanValid(int resultId) { return mTaskChangeId == resultId; } @@ -178,6 +211,19 @@ public class RecentsModel extends TaskStackChangeListener { return mSystemUiProxy; } + public void onStart() { + mRecentsTaskLoader.startLoader(mContext); + mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(true); + } + + public void onTrimMemory(int level) { + if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { + // We already stop the loader in UI_HIDDEN, so stop the high res loader as well + mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(false); + } + mRecentsTaskLoader.onTrimMemory(level); + } + @WorkerThread public void preloadAssistData(int taskId, Bundle data) { mMainThreadExecutor.execute(() -> { diff --git a/quickstep/src/com/android/quickstep/RecentsRootView.java b/quickstep/src/com/android/quickstep/RecentsRootView.java new file mode 100644 index 0000000000..24785f9316 --- /dev/null +++ b/quickstep/src/com/android/quickstep/RecentsRootView.java @@ -0,0 +1,58 @@ +/* + * 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.Context; +import android.graphics.Rect; +import android.util.AttributeSet; + +import com.android.launcher3.BaseActivity; +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 BaseDragLayer { + + private final BaseActivity mActivity; + + public RecentsRootView(Context context, AttributeSet attrs) { + super(context, attrs); + mActivity = BaseActivity.fromContext(context); + mControllers = new TouchController[0]; + } + + @TargetApi(23) + @Override + protected boolean fitSystemWindows(Rect insets) { + // Update device profile before notifying the children. + mActivity.getDeviceProfile().updateInsets(insets); + setInsets(insets); + return true; // I'll take it from here + } + + @Override + public void setInsets(Rect insets) { + // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by + // modifying child layout params. + if (!insets.equals(mInsets)) { + super.setInsets(insets); + } + setBackground(insets.top == 0 ? null + : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim)); + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java deleted file mode 100644 index ec0716c49c..0000000000 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ /dev/null @@ -1,564 +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.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.quickstep.TaskView.CURVE_FACTOR; -import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR; - -import android.animation.LayoutTransition; -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.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Insettable; -import com.android.launcher3.Launcher; -import com.android.launcher3.PagedView; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.uioverrides.OverviewState; -import com.android.launcher3.uioverrides.RecentsViewStateController; -import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; -import com.android.systemui.shared.recents.model.RecentsTaskLoader; -import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.recents.model.TaskStack; -import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.shared.system.WindowManagerWrapper; - -import java.util.ArrayList; - -/** - * A list of recent tasks. - */ -public class RecentsView extends PagedView implements Insettable { - - private static final Rect sTempStableInsets = new Rect(); - - public static final int SCROLL_TYPE_NONE = 0; - public static final int SCROLL_TYPE_TASK = 1; - public static final int SCROLL_TYPE_WORKSPACE = 2; - - private final Launcher mLauncher; - private QuickScrubController mQuickScrubController; - private final ScrollState mScrollState = new ScrollState(); - private boolean mOverviewStateEnabled; - private boolean mTaskStackListenerRegistered; - private LayoutTransition mLayoutTransition; - private Runnable mNextPageSwitchRunnable; - - /** - * TODO: Call reloadIdNeeded in onTaskStackChanged. - */ - private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { - @Override - public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { - for (int i = mFirstTaskIndex; i < getChildCount(); i++) { - final TaskView taskView = (TaskView) getChildAt(i); - if (taskView.getTask().key.id == taskId) { - taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot); - return; - } - } - } - }; - - private RecentsViewStateController mStateController; - private int mFirstTaskIndex; - - private final RecentsModel mModel; - private int mLoadPlanId = -1; - - // Only valid until the launcher state changes to NORMAL - private int mRunningTaskId = -1; - - private Bitmap mScrim; - private Paint mFadePaint; - private Shader mFadeShader; - private Matrix mFadeMatrix; - private boolean mScrimOnLeft; - - private boolean mFirstTaskIconScaledDown = false; - - public RecentsView(Context context) { - this(context, null); - } - - public RecentsView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing)); - enableFreeScroll(true); - setupLayoutTransition(); - setClipToOutline(true); - - mLauncher = Launcher.getLauncher(context); - mQuickScrubController = new QuickScrubController(mLauncher, this); - mModel = RecentsModel.getInstance(context); - - mScrollState.isRtl = mIsRtl; - } - - public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { - for (int i = mFirstTaskIndex; i < getChildCount(); i++) { - final TaskView taskView = (TaskView) getChildAt(i); - if (taskView.getTask().key.id == taskId) { - taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData); - taskView.setAlpha(1); - return taskView; - } - } - return null; - } - - private void setupLayoutTransition() { - // We want to show layout transitions when pages are deleted, to close the gap. - mLayoutTransition = new LayoutTransition(); - mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING); - mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - - mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING); - mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); - setLayoutTransition(mLayoutTransition); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mFirstTaskIndex = getPageCount(); - } - - @Override - protected void onWindowVisibilityChanged(int visibility) { - super.onWindowVisibilityChanged(visibility); - updateTaskStackListenerState(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - updateTaskStackListenerState(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - updateTaskStackListenerState(); - } - - @Override - public void setInsets(Rect insets) { - mInsets.set(insets); - DeviceProfile dp = Launcher.getLauncher(getContext()).getDeviceProfile(); - Rect padding = getPadding(dp, getContext()); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - lp.bottomMargin = padding.bottom; - 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; - } - } - - public int getFirstTaskIndex() { - return mFirstTaskIndex; - } - - public boolean isTaskViewVisible(TaskView tv) { - // For now, just check if it's the active task or an adjacent task - return Math.abs(indexOfChild(tv) - getNextPage()) <= 1; - } - - public TaskView getTaskView(int taskId) { - for (int i = getFirstTaskIndex(); i < getChildCount(); i++) { - TaskView tv = (TaskView) getChildAt(i); - if (tv.getTask().key.id == taskId) { - return tv; - } - } - return null; - } - - public void setStateController(RecentsViewStateController stateController) { - mStateController = stateController; - } - - public RecentsViewStateController getStateController() { - return mStateController; - } - - public void setOverviewStateEnabled(boolean enabled) { - mOverviewStateEnabled = enabled; - updateTaskStackListenerState(); - } - - public void setNextPageSwitchRunnable(Runnable r) { - mNextPageSwitchRunnable = r; - } - - @Override - protected void onPageEndTransition() { - super.onPageEndTransition(); - if (mNextPageSwitchRunnable != null) { - mNextPageSwitchRunnable.run(); - mNextPageSwitchRunnable = null; - } - } - - private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) { - final RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); - TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null; - if (stack == null) { - removeAllViews(); - return; - } - - int oldChildCount = getChildCount(); - - // Ensure there are as many views as there are tasks in the stack (adding and trimming as - // necessary) - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final ArrayList tasks = new ArrayList<>(stack.getTasks()); - setLayoutTransition(null); - - final int requiredChildCount = tasks.size() + mFirstTaskIndex; - for (int i = getChildCount(); i < requiredChildCount; i++) { - final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false); - addView(taskView); - } - while (getChildCount() > requiredChildCount) { - final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1); - removeView(taskView); - loader.unloadTaskData(taskView.getTask()); - } - setLayoutTransition(mLayoutTransition); - - // Rebind and reset all task views - for (int i = tasks.size() - 1; i >= 0; i--) { - final Task task = tasks.get(i); - final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1 + mFirstTaskIndex); - taskView.bind(task); - taskView.resetVisualProperties(); - loader.loadTaskData(task); - } - updateCurveProperties(); - applyIconScale(false /* animate */); - - if (oldChildCount != getChildCount()) { - mQuickScrubController.snapToPageForCurrentQuickScrubSection(); - } - } - - private void updateTaskStackListenerState() { - boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow() - && getWindowVisibility() == VISIBLE; - if (registerStackListener != mTaskStackListenerRegistered) { - if (registerStackListener) { - ActivityManagerWrapper.getInstance() - .registerTaskStackListener(mTaskStackListener); - reloadIfNeeded(); - } else { - ActivityManagerWrapper.getInstance() - .unregisterTaskStackListener(mTaskStackListener); - } - mTaskStackListenerRegistered = registerStackListener; - } - } - - private static Rect getPadding(DeviceProfile profile, Context context) { - WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets); - Rect padding = new Rect(profile.workspacePadding); - - float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right; - float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom; - - float overviewHeight, overviewWidth; - if (profile.isVerticalBarLayout()) { - // Use the same padding on both sides for symmetry. - float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right); - float availableHeight = profile.availableHeightPx - padding.top - - sTempStableInsets.top - Math.max(padding.bottom, - profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context))); - float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight); - overviewHeight = taskHeight * scaledRatio; - overviewWidth = taskWidth * scaledRatio; - - } else { - overviewHeight = profile.availableHeightPx - padding.top - padding.bottom - - sTempStableInsets.top; - overviewWidth = taskWidth * overviewHeight / taskHeight; - } - - padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top - - Math.round(overviewHeight); - padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2); - return padding; - } - - /** - * Sets the {@param outRect} to match the position of the first tile such that it is scaled - * down to match the 2nd taskView. - * @return returns the factor which determines the scaling factor for the second task. - */ - public static float getScaledDownPageRect(DeviceProfile dp, Context context, Rect outRect) { - getPageRect(dp, context, outRect); - - int pageSpacing = context.getResources() - .getDimensionPixelSize(R.dimen.recents_page_spacing); - float halfScreenWidth = dp.widthPx * 0.5f; - float halfPageWidth = outRect.width() * 0.5f; - float pageCenter = outRect.right + pageSpacing + halfPageWidth; - float distanceFromCenter = Math.abs(halfScreenWidth - pageCenter); - float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing; - float linearInterpolation = Math.min(1, distanceFromCenter / distanceToReachEdge); - - float scale = 1 - CURVE_INTERPOLATOR.getInterpolation(linearInterpolation) * CURVE_FACTOR; - - int topMargin = context.getResources() - .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); - outRect.top -= topMargin; - Utilities.scaleRectAboutCenter(outRect, scale); - outRect.top += (int) (scale * topMargin); - return linearInterpolation; - } - - public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) { - Rect targetPadding = getPadding(grid, context); - 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); - outRect.top += context.getResources() - .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); - } - - @Override - public void computeScroll() { - super.computeScroll(); - updateCurveProperties(); - } - - /** - * Scales and adjusts translation of adjacent pages as if on a curved carousel. - */ - public void updateCurveProperties() { - if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { - return; - } - final int halfPageWidth = mScrollState.halfPageWidth = getNormalChildWidth() / 2; - mScrollState.lastScrollType = SCROLL_TYPE_NONE; - final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth; - final int halfScreenWidth = getMeasuredWidth() / 2; - final int pageSpacing = mPageSpacing; - - final int pageCount = getPageCount(); - for (int i = 0; i < pageCount; i++) { - View page = getPageAt(i); - int pageCenter = page.getLeft() + halfPageWidth; - mScrollState.distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing; - mScrollState.linearInterpolation = Math.min(1, - Math.abs(mScrollState.distanceFromScreenCenter) / distanceToReachEdge); - mScrollState.lastScrollType = ((PageCallbacks) page).onPageScroll(mScrollState); - } - } - - public void onTaskDismissed(TaskView taskView) { - ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id); - removeView(taskView); - if (getTaskCount() == 0) { - mLauncher.getStateManager().goToState(NORMAL); - } - } - - public void reset() { - mRunningTaskId = -1; - setCurrentPage(0); - } - - public int getTaskCount() { - return getChildCount() - mFirstTaskIndex; - } - - public int getRunningTaskId() { - return mRunningTaskId; - } - - /** - * Reloads the view if anything in recents changed. - */ - public void reloadIfNeeded() { - if (!mModel.isLoadPlanValid(mLoadPlanId)) { - mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan); - } - } - - /** - * Ensures that the first task in the view represents {@param task} and reloads the view - * if needed. This allows the swipe-up gesture to assume that the first tile always - * corresponds to the correct task. - * All subsequent calls to reload will keep the task as the first item until {@link #reset()} - * is called. - * Also scrolls the view to this task - */ - public void showTask(int runningTaskId) { - boolean needsReload = false; - if (getTaskCount() == 0) { - needsReload = true; - // Add an empty view for now - setLayoutTransition(null); - final TaskView taskView = (TaskView) LayoutInflater.from(getContext()) - .inflate(R.layout.task, this, false); - addView(taskView, mFirstTaskIndex); - setLayoutTransition(mLayoutTransition); - } - if (!needsReload) { - needsReload = !mModel.isLoadPlanValid(mLoadPlanId); - } - if (needsReload) { - mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan); - } - mRunningTaskId = runningTaskId; - setCurrentPage(mFirstTaskIndex); - if (mCurrentPage >= mFirstTaskIndex) { - getPageAt(mCurrentPage).setAlpha(0); - } - } - - public QuickScrubController getQuickScrubController() { - return mQuickScrubController; - } - - public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) { - if (mFirstTaskIconScaledDown == isScaledDown) { - return; - } - mFirstTaskIconScaledDown = isScaledDown; - applyIconScale(animate); - } - - private void applyIconScale(boolean animate) { - float scale = mFirstTaskIconScaledDown ? 0 : 1; - TaskView firstTask = (TaskView) getChildAt(mFirstTaskIndex); - if (firstTask != null) { - if (animate) { - firstTask.animateIconToScale(scale); - } else { - firstTask.setIconScale(scale); - } - } - } - - @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); - } - - public interface PageCallbacks { - - /** - * Updates the page UI based on scroll params and returns the type of scroll - * effect performed. - * - * @see #SCROLL_TYPE_NONE - * @see #SCROLL_TYPE_TASK - * @see #SCROLL_TYPE_WORKSPACE - */ - int onPageScroll(ScrollState scrollState); - } - - public static class ScrollState { - - public boolean isRtl; - public int lastScrollType; - - public int halfPageWidth; - public float distanceFromScreenCenter; - public float linearInterpolation; - - public float prevPageExtraWidth; - } -} diff --git a/quickstep/src/com/android/quickstep/SimpleTaskView.java b/quickstep/src/com/android/quickstep/SimpleTaskView.java deleted file mode 100644 index 8425fa3ba4..0000000000 --- a/quickstep/src/com/android/quickstep/SimpleTaskView.java +++ /dev/null @@ -1,52 +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.content.Context; -import android.graphics.Point; -import android.util.AttributeSet; -import android.view.View; -import android.view.WindowManager; - -/** - * A simple view which keeps its size proportional to the display size - */ -public class SimpleTaskView extends View { - - private static final Point sTempPoint = new Point(); - - public SimpleTaskView(Context context) { - super(context); - } - - public SimpleTaskView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public SimpleTaskView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int height = MeasureSpec.getSize(heightMeasureSpec); - getContext().getSystemService(WindowManager.class) - .getDefaultDisplay().getRealSize(sTempPoint); - - int width = (int) ((float) height * sTempPoint.x / sTempPoint.y); - setMeasuredDimension(width, height); - } -} 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 75d8619eae..2ebf2525f5 100644 --- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java @@ -16,27 +16,41 @@ package com.android.quickstep; -import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Rect; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; 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; +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.ActivityOptionsCompat; +import com.android.systemui.shared.system.WindowManagerWrapper; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; /** @@ -58,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(); @@ -70,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 { @@ -86,9 +101,13 @@ public class TaskSystemShortcut extends SystemShortcut } } - public static class SplitScreen extends TaskSystemShortcut { + 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); @@ -96,30 +115,95 @@ 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 -> { - final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(true); - final Consumer resultCallback = success -> { - if (success) { - launcher.getOverviewPanel().removeView(taskView); - } - }; - ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync( - task.key, options, resultCallback, mHandler); + AbstractFloatingView.closeOpenViews(activity, true, + AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); - // TODO improve transition; see: - // DockedFirstAnimationFrameEvent - // RecentsTransitionHelper#composeDockAnimationSpec - // WindowManagerWrapper#overridePendingAppTransitionMultiThumbFuture + if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId, + ActivityOptionsCompat.makeSplitScreenOptions(true))) { + ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy(); + try { + sysUiProxy.onSplitScreenInvoked(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify SysUI of split screen: ", e); + 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); + }; + + final int[] position = new int[2]; + 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(), thumbnailView, 1f, + Color.BLACK); + AppTransitionAnimationSpecsFuture future = + new AppTransitionAnimationSpecsFuture(mHandler) { + @Override + public List composeSpecs() { + return Collections.singletonList(new AppTransitionAnimationSpecCompat( + taskId, thumbnail, taskBounds)); + } + }; + WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( + future, animStartedListener, mHandler, true /* scaleUp */); + } }); } + + @Override + public boolean onPreDraw() { + mTaskView.getViewTreeObserver().removeOnPreDrawListener(this); + 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 { @@ -132,12 +216,17 @@ 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; } - if (!ActivityManagerWrapper.getInstance().isLockToAppEnabled()) { + if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) { + return null; + } + if (ActivityManagerWrapper.getInstance().isLockToAppActive()) { + // We shouldn't be able to pin while an app is locked. return null; } return view -> { @@ -161,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 f35f6a6f45..4e351595ff 100644 --- a/quickstep/src/com/android/quickstep/TouchConsumer.java +++ b/quickstep/src/com/android/quickstep/TouchConsumer.java @@ -21,8 +21,6 @@ import android.support.annotation.IntDef; import android.view.Choreographer; import android.view.MotionEvent; -import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; @@ -31,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() { } @@ -64,4 +55,16 @@ public interface TouchConsumer extends Consumer { default Choreographer getIntrimChoreographer(MotionEventQueue queue) { return null; } + + default void deferInit() { } + + default boolean deferNextEventToMainThread() { + return false; + } + + 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 c16629279e..cc49dc743e 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -21,15 +21,14 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION; +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; import android.app.Service; -import android.content.ComponentName; import android.content.Intent; -import android.content.pm.ResolveInfo; import android.graphics.PointF; import android.os.Build; import android.os.Handler; @@ -40,15 +39,17 @@ import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; 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.uioverrides.UiFactory; import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -60,6 +61,8 @@ import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; @TargetApi(Build.VERSION_CODES.O) public class TouchInteractionService extends Service { + public static final boolean DEBUG_OPEN_OVERVIEW_VIA_ALT_TAB = false; + private static final SparseArray sMotionEventNames; static { @@ -83,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); } @@ -103,21 +106,12 @@ 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 public void onQuickScrubStart() { mEventQueue.onQuickScrubStart(); - sQuickScrubEnabled = true; TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart"); } @@ -130,35 +124,57 @@ public class TouchInteractionService extends Service { public void onQuickScrubEnd() { mEventQueue.onQuickScrubEnd(); TraceHelper.endSection("SysUiBinder", "onQuickScrubEnd"); - sQuickScrubEnabled = false; + } + + @Override + public void onOverviewToggle() { + mOverviewCommandHelper.onOverviewToggle(); + } + + @Override + 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) { + 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) -> {}; private static boolean sConnected = false; - private static boolean sQuickScrubEnabled = false; public static boolean isConnected() { return sConnected; } - public static boolean isQuickScrubEnabled() { - return sQuickScrubEnabled; - } - private ActivityManagerWrapper mAM; - private RunningTaskInfo mRunningTask; private RecentsModel mRecentsModel; - private Intent mHomeIntent; - private ComponentName mLauncher; private MotionEventQueue mEventQueue; private MainThreadExecutor mMainThreadExecutor; private ISystemUiProxy mISystemUiProxy; + private OverviewCommandHelper mOverviewCommandHelper; + private OverviewInteractionState mOverviewInteractionState; private Choreographer mMainThreadChoreographer; private Choreographer mBackgroundThreadChoreographer; - private MotionEventQueue mNoOpEventQueue; @Override public void onCreate() { @@ -166,19 +182,10 @@ public class TouchInteractionService extends Service { mAM = ActivityManagerWrapper.getInstance(); mRecentsModel = RecentsModel.getInstance(this); mMainThreadExecutor = new MainThreadExecutor(); - - mHomeIntent = new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_HOME) - .setPackage(getPackageName()) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0); - mLauncher = new ComponentName(getPackageName(), info.activityInfo.name); - // Clear the packageName as system can fail to dedupe it b/64108432 - mHomeIntent.setComponent(mLauncher).setPackage(null); - + mOverviewCommandHelper = new OverviewCommandHelper(this); mMainThreadChoreographer = Choreographer.getInstance(); - mNoOpEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer); - mEventQueue = mNoOpEventQueue; + mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer); + mOverviewInteractionState = OverviewInteractionState.getInstance(this); sConnected = true; @@ -190,7 +197,6 @@ public class TouchInteractionService extends Service { @Override public void onDestroy() { sConnected = false; - sQuickScrubEnabled = false; super.onDestroy(); } @@ -200,32 +206,47 @@ public class TouchInteractionService extends Service { return mMyBinder; } - private void onBinderPreMotionEvent(@HitTarget int downHitTarget) { - mRunningTask = mAM.getRunningTask(); - + private void setupTouchConsumer(@HitTarget int downHitTarget) { mEventQueue.reset(); - - if (mRunningTask == null) { - mEventQueue = mNoOpEventQueue; - } else if (mRunningTask.topActivity.equals(mLauncher)) { - mEventQueue = getLauncherEventQueue(); - } else { + TouchConsumer oldConsumer = mEventQueue.getConsumer(); + if (oldConsumer.deferNextEventToMainThread()) { mEventQueue = new MotionEventQueue(mMainThreadChoreographer, - new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel, - mHomeIntent, mISystemUiProxy, mMainThreadExecutor, - mBackgroundThreadChoreographer, downHitTarget)); + new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget, + oldConsumer.forceToLauncherConsumer(), v))); + mEventQueue.deferInit(); + } else { + mEventQueue = new MotionEventQueue( + mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null)); } } - private MotionEventQueue getLauncherEventQueue() { + private TouchConsumer getCurrentTouchConsumer( + @HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) { + RunningTaskInfo runningTaskInfo = mAM.getRunningTask(); + + if (runningTaskInfo == null && !forceToLauncher) { + return mNoOpTouchConsumer; + } else if (forceToLauncher || + runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) { + return getLauncherConsumer(); + } else { + if (tracker == null) { + tracker = VelocityTracker.obtain(); + } + return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel, + mOverviewCommandHelper.homeIntent, + mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor, + mBackgroundThreadChoreographer, downHitTarget, tracker); + } + } + + private TouchConsumer getLauncherConsumer() { Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback(); if (launcher == null) { - return mNoOpEventQueue; + return mNoOpTouchConsumer; } - View target = launcher.getDragLayer(); - return new MotionEventQueue(mMainThreadChoreographer, - new LauncherTouchConsumer(launcher, target)); + return new LauncherTouchConsumer(launcher, target); } private static class LauncherTouchConsumer implements TouchConsumer { @@ -307,16 +328,11 @@ 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_SCRUB) { - mQuickScrubController.onQuickScrubStart(true); - } else if (interactionType == INTERACTION_QUICK_SWITCH) { - onComplete = mQuickScrubController::onQuickSwitch; - } - mLauncher.getStateManager().goToState(OVERVIEW, true, 0, - QUICK_SWITCH_START_DURATION, onComplete); + LauncherState fromState = mLauncher.getStateManager().getState(); + mLauncher.getStateManager().goToState(FAST_OVERVIEW, true); + mQuickScrubController.onQuickScrubStart(fromState == NORMAL); }; if (mLauncher.getWorkspace().runOnOverlayHidden(action)) { diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 19942c3950..25f2f87601 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -15,24 +15,20 @@ */ package com.android.quickstep; -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.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; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; @@ -42,21 +38,21 @@ import android.graphics.RectF; import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.support.annotation.UiThread; 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; @@ -65,11 +61,16 @@ 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; import com.android.systemui.shared.recents.utilities.RectFEvaluator; import com.android.systemui.shared.system.InputConsumerController; +import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.TransactionCompat; @@ -78,7 +79,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.util.StringJoiner; @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; @@ -101,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 @@ -152,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; @@ -165,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; @@ -189,11 +193,21 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper(); private Matrix mTmpMatrix = new Matrix(); + private final long mTouchTimeMs; + private long mLauncherFrameDrawnTime; - WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) { + WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, + ActivityControlHelper controller) { mContext = context; mRunningTaskId = runningTaskInfo.id; - mInputConsumer.registerInputConsumer(); + 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); initStateCallbacks(); } @@ -236,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); } @@ -263,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, @@ -284,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() { @@ -306,39 +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.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); - 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) { @@ -346,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() { @@ -379,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) { @@ -401,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; @@ -416,11 +415,19 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { if (mLauncherDrawnCallback != null) { mLauncherDrawnCallback.run(); } + mLauncherFrameDrawnTime = SystemClock.uptimeMillis(); } private void initializeLauncherAnimationController() { - mLauncherLayoutListener.setHandler(this); + mLayoutListener.setHandler(this); onLauncherLayoutChanged(); + + final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs; + SysuiEventLogger.writeDummyRecentsTransition(transitionDelay); + + if (LatencyTrackerCompat.isEnabled(mContext)) { + LatencyTrackerCompat.logToggleRecents((int) transitionDelay); + } } public void updateInteractionType(@InteractionType int interactionType) { @@ -428,21 +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() { - mQuickScrubController.onQuickScrubStart(false); + animateToProgress(1f, QUICK_SCRUB_START_DURATION); } @WorkerThread @@ -455,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); } } @@ -493,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); @@ -509,8 +498,13 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) { if (app.mode == MODE_CLOSING) { transaction.setMatrix(app.leash, mTmpMatrix) - .setWindowCrop(app.leash, mClipRect) - .show(app.leash); + .setWindowCrop(app.leash, mClipRect); + + if (app.isNotInRecents) { + transaction.setAlpha(app.leash, 1 - shift); + } + + transaction.show(app.leash); } } transaction.apply(); @@ -525,17 +519,21 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mLauncherTransitionController.setPlayFraction(shift); // Make sure the window follows the first task if it moves, e.g. during quick scrub. - int firstTaskIndex = mRecentsView.getFirstTaskIndex(); - View firstTask = mRecentsView.getPageAt(firstTaskIndex); - int scrollForFirstTask = mRecentsView.getScrollForPage(firstTaskIndex); + 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()) { @@ -583,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); } @@ -606,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); } } @@ -658,13 +656,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { /** 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) { - setStateOnUiThread((Float.compare(mCurrentShift.value, 0) == 0) - ? STATE_SCALED_CONTROLLER_APP : STATE_SCALED_CONTROLLER_RECENTS); + setStateOnUiThread(mIsGoingToHome ? + STATE_SCALED_CONTROLLER_RECENTS : STATE_SCALED_CONTROLLER_APP); } }); anim.start(); @@ -685,30 +684,26 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } private void invalidateHandler() { - mCurrentShift.cancelAnimation(); + mCurrentShift.finishAnimation(); if (mGestureEndCallback != null) { mGestureEndCallback.run(); } - clearReference(); + mActivityInitListener.unregister(); mInputConsumer.unregisterInputConsumer(); } private void invalidateHandlerWithLauncher() { mLauncherTransitionController = null; - mLauncherLayoutListener.setHandler(null); - mLauncherLayoutListener.close(false); + mLayoutListener.finish(); - // Restore the requested orientation to the user preference after the gesture has ended - mLauncher.updateRequestedOrientation(); 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() { @@ -737,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; } } @@ -763,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 */); @@ -772,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) { @@ -799,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; @@ -818,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 new file mode 100644 index 0000000000..861b5fa038 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -0,0 +1,129 @@ +/* + * 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.views; + +import static com.android.launcher3.LauncherState.NORMAL; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Rect; +import android.os.Build; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.widget.FrameLayout; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; + +/** + * {@link RecentsView} used in Launcher activity + */ +@TargetApi(Build.VERSION_CODES.O) +public class LauncherRecentsView extends RecentsView implements Insettable { + + 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); + } + + public LauncherRecentsView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setContentAlpha(0); + } + + @Override + public void setInsets(Rect insets) { + mInsets.set(insets); + DeviceProfile dp = mActivity.getDeviceProfile(); + Rect padding = getPadding(dp, getContext()); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + lp.bottomMargin = padding.bottom; + setLayoutParams(lp); + + setPadding(padding.left, padding.top, padding.right, 0); + 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 new file mode 100644 index 0000000000..8901e6d8c6 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -0,0 +1,711 @@ +/* + * 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.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; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.content.Context; +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; + +import com.android.launcher3.BaseActivity; +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; +import com.android.quickstep.RecentsModel; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.WindowManagerWrapper; + +import java.util.ArrayList; + +/** + * A list of recent tasks. + */ +@TargetApi(Build.VERSION_CODES.P) +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(); + + protected final T mActivity; + private final QuickScrubController mQuickScrubController; + private final float mFastFlingVelocity; + private final RecentsModel mModel; + + private final ScrollState mScrollState = new ScrollState(); + // Keeps track of the previously known visible tasks for purposes of loading/unloading task data + private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray(); + + /** + * TODO: Call reloadIdNeeded in onTaskStackChanged. + */ + private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + @Override + public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { + for (int i = 0; i < getChildCount(); i++) { + final TaskView taskView = (TaskView) getChildAt(i); + if (taskView.getTask().key.id == taskId) { + taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot); + return; + } + } + } + }; + + private int mLoadPlanId = -1; + + // Only valid until the launcher state changes to NORMAL + private int mRunningTaskId = -1; + + private boolean mFirstTaskIconScaledDown = false; + + private boolean mOverviewStateEnabled; + private boolean mTaskStackListenerRegistered; + private Runnable mNextPageSwitchRunnable; + + 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)); + enableFreeScroll(true); + setClipToOutline(true); + + mFastFlingVelocity = getResources() + .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity); + mActivity = (T) BaseActivity.fromContext(context); + mQuickScrubController = new QuickScrubController(mActivity, this); + mModel = RecentsModel.getInstance(context); + + onSharedPreferenceChanged(Utilities.getPrefs(context), PREF_FLIP_RECENTS); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { + if (s.equals(PREF_FLIP_RECENTS)) { + mIsRtl = Utilities.isRtl(getResources()); + if (sharedPreferences.getBoolean(PREF_FLIP_RECENTS, false)) { + mIsRtl = !mIsRtl; + } + setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); + } + } + + public boolean isRtl() { + return mIsRtl; + } + + public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { + for (int i = 0; i < getChildCount(); i++) { + final TaskView taskView = (TaskView) getChildAt(i); + if (taskView.getTask().key.id == taskId) { + taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData); + taskView.setAlpha(1); + return taskView; + } + } + return null; + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + updateTaskStackListenerState(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updateTaskStackListenerState(); + Utilities.getPrefs(getContext()).registerOnSharedPreferenceChangeListener(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + updateTaskStackListenerState(); + Utilities.getPrefs(getContext()).unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onViewRemoved(View child) { + super.onViewRemoved(child); + + // Clear the task data for the removed child if it was visible + Task task = ((TaskView) child).getTask(); + if (mHasVisibleTaskData.get(task.key.id)) { + mHasVisibleTaskData.delete(task.key.id); + RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); + loader.unloadTaskData(task); + loader.getHighResThumbnailLoader().onTaskInvisible(task); + } + } + + public boolean isTaskViewVisible(TaskView tv) { + // For now, just check if it's the active task or an adjacent task + return Math.abs(indexOfChild(tv) - getNextPage()) <= 1; + } + + public TaskView getTaskView(int taskId) { + for (int i = 0; i < getChildCount(); i++) { + TaskView tv = (TaskView) getChildAt(i); + if (tv.getTask().key.id == taskId) { + return tv; + } + } + return null; + } + + public void setOverviewStateEnabled(boolean enabled) { + mOverviewStateEnabled = enabled; + updateTaskStackListenerState(); + } + + public void setNextPageSwitchRunnable(Runnable r) { + mNextPageSwitchRunnable = r; + } + + @Override + protected void onPageEndTransition() { + super.onPageEndTransition(); + if (mNextPageSwitchRunnable != null) { + mNextPageSwitchRunnable.run(); + mNextPageSwitchRunnable = null; + } + } + + private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) { + if (mPendingAnimation != null) { + mPendingAnimation.addEndListener((b) -> applyLoadPlan(loadPlan)); + return; + } + TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null; + if (stack == null) { + removeAllViews(); + return; + } + + int oldChildCount = getChildCount(); + + // Ensure there are as many views as there are tasks in the stack (adding and trimming as + // necessary) + final LayoutInflater inflater = LayoutInflater.from(getContext()); + final ArrayList tasks = new ArrayList<>(stack.getTasks()); + + final int requiredChildCount = tasks.size(); + for (int i = getChildCount(); i < requiredChildCount; i++) { + final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false); + addView(taskView); + } + while (getChildCount() > requiredChildCount) { + final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1); + removeView(taskView); + } + + // Unload existing visible task data + unloadVisibleTaskData(); + + // Rebind and reset all task views + for (int i = requiredChildCount - 1; i >= 0; i--) { + final int pageIndex = requiredChildCount - i - 1; + final Task task = tasks.get(i); + final TaskView taskView = (TaskView) getChildAt(pageIndex); + taskView.bind(task); + } + resetTaskVisuals(); + applyIconScale(false /* animate */); + + if (oldChildCount != getChildCount()) { + mQuickScrubController.snapToNextTaskIfAvailable(); + } + } + + public void resetTaskVisuals() { + for (int i = getChildCount() - 1; i >= 0; i--) { + TaskView taskView = (TaskView) getChildAt(i); + if (!mIgnoreResetTaskViews.contains(taskView)) { + taskView.resetVisualProperties(); + } + } + + updateCurveProperties(); + // Update the set of visible task's data + loadVisibleTaskData(); + } + + private void updateTaskStackListenerState() { + boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow() + && getWindowVisibility() == VISIBLE; + if (registerStackListener != mTaskStackListenerRegistered) { + if (registerStackListener) { + ActivityManagerWrapper.getInstance() + .registerTaskStackListener(mTaskStackListener); + reloadIfNeeded(); + } else { + ActivityManagerWrapper.getInstance() + .unregisterTaskStackListener(mTaskStackListener); + } + mTaskStackListenerRegistered = registerStackListener; + } + } + + protected static Rect getPadding(DeviceProfile profile, Context context) { + WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets); + Rect padding = new Rect(profile.workspacePadding); + + float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right; + float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom; + + float overviewHeight, overviewWidth; + if (profile.isVerticalBarLayout()) { + float maxPadding = Math.max(padding.left, padding.right); + + // Use the same padding on both sides for symmetry. + float availableWidth = taskWidth - 2 * maxPadding; + float availableHeight = profile.availableHeightPx - padding.top - padding.bottom + - sTempStableInsets.top; + float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight); + overviewHeight = taskHeight * scaledRatio; + overviewWidth = taskWidth * scaledRatio; + + } else { + overviewHeight = profile.availableHeightPx - padding.top - padding.bottom + - sTempStableInsets.top; + overviewWidth = taskWidth * overviewHeight / taskHeight; + } + + padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top + - Math.round(overviewHeight); + padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2); + return padding; + } + + public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) { + Rect targetPadding = getPadding(grid, context); + 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); + outRect.top += context.getResources() + .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); + } + + @Override + protected boolean computeScrollHelper() { + boolean scrolling = super.computeScrollHelper(); + boolean isFlingingFast = false; + updateCurveProperties(); + if (scrolling || (mTouchState == TOUCH_STATE_SCROLLING)) { + if (scrolling) { + // Check if we are flinging quickly to disable high res thumbnail loading + isFlingingFast = mScroller.getCurrVelocity() > mFastFlingVelocity; + } + + // After scrolling, update the visible task's data + loadVisibleTaskData(); + } + + // Update the high res thumbnail loader + RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); + loader.getHighResThumbnailLoader().setFlingingFast(isFlingingFast); + return scrolling; + } + + /** + * Scales and adjusts translation of adjacent pages as if on a curved carousel. + */ + public void updateCurveProperties() { + if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { + return; + } + final int halfPageWidth = getNormalChildWidth() / 2; + final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth; + final int halfScreenWidth = getMeasuredWidth() / 2; + final int pageSpacing = mPageSpacing; + + final int pageCount = getPageCount(); + for (int i = 0; i < pageCount; i++) { + View page = getPageAt(i); + float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth; + float distanceFromScreenCenter = screenCenter - pageCenter; + float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing; + mScrollState.linearInterpolation = Math.min(1, + Math.abs(distanceFromScreenCenter) / distanceToReachEdge); + ((PageCallbacks) page).onPageScroll(mScrollState); + } + } + + /** + * Iterates through all thet asks, and loads the associated task data for newly visible tasks, + * and unloads the associated task data for tasks that are no longer visible. + */ + public void loadVisibleTaskData() { + RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); + int centerPageIndex = getPageNearestToCenterOfScreen(); + int lower = Math.max(0, centerPageIndex - 2); + int upper = Math.min(centerPageIndex + 2, getChildCount() - 1); + int numChildren = getChildCount(); + + // Update the task data for the in/visible children + for (int i = 0; i < numChildren; i++) { + TaskView taskView = (TaskView) getChildAt(i); + Task task = taskView.getTask(); + boolean visible = lower <= i && i <= upper; + if (visible) { + if (!mHasVisibleTaskData.get(task.key.id)) { + loader.loadTaskData(task); + loader.getHighResThumbnailLoader().onTaskVisible(task); + } + mHasVisibleTaskData.put(task.key.id, visible); + } else { + if (mHasVisibleTaskData.get(task.key.id)) { + loader.unloadTaskData(task); + loader.getHighResThumbnailLoader().onTaskInvisible(task); + } + mHasVisibleTaskData.delete(task.key.id); + } + } + } + + /** + * Unloads any associated data from the currently visible tasks + */ + private void unloadVisibleTaskData() { + RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); + for (int i = 0; i < mHasVisibleTaskData.size(); i++) { + if (mHasVisibleTaskData.valueAt(i)) { + TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i)); + Task task = taskView.getTask(); + loader.unloadTaskData(task); + loader.getHighResThumbnailLoader().onTaskInvisible(task); + } + } + mHasVisibleTaskData.clear(); + } + + + protected abstract void onAllTasksRemoved(); + + public void reset() { + unloadVisibleTaskData(); + mRunningTaskId = -1; + setCurrentPage(0); + } + + /** + * Reloads the view if anything in recents changed. + */ + public void reloadIfNeeded() { + if (!mModel.isLoadPlanValid(mLoadPlanId)) { + mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan); + } + } + + /** + * Ensures that the first task in the view represents {@param task} and reloads the view + * if needed. This allows the swipe-up gesture to assume that the first tile always + * corresponds to the correct task. + * All subsequent calls to reload will keep the task as the first item until {@link #reset()} + * is called. + * Also scrolls the view to this task + */ + public void showTask(int runningTaskId) { + boolean needsReload = false; + if (getChildCount() == 0) { + needsReload = true; + // Add an empty view for now + final TaskView taskView = (TaskView) LayoutInflater.from(getContext()) + .inflate(R.layout.task, this, false); + addView(taskView, 0); + } + mRunningTaskId = runningTaskId; + setCurrentPage(0); + if (!needsReload) { + needsReload = !mModel.isLoadPlanValid(mLoadPlanId); + } + if (needsReload) { + mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan); + } else { + loadVisibleTaskData(); + } + getPageAt(mCurrentPage).setAlpha(0); + } + + public QuickScrubController getQuickScrubController() { + return mQuickScrubController; + } + + public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) { + if (mFirstTaskIconScaledDown == isScaledDown) { + return; + } + mFirstTaskIconScaledDown = isScaledDown; + applyIconScale(animate); + } + + private void applyIconScale(boolean animate) { + float scale = mFirstTaskIconScaledDown ? 0 : 1; + TaskView firstTask = (TaskView) getChildAt(0); + if (firstTask != null) { + if (animate) { + firstTask.animateIconToScale(scale); + } else { + firstTask.setIconScale(scale); + } + } + } + + public interface PageCallbacks { + + /** + * Updates the page UI based on scroll params. + */ + default void onPageScroll(ScrollState scrollState) {}; + } + + public static class ScrollState { + + /** + * The progress from 0 to 1, where 0 is the center + * of the screen and 1 is the edge of the screen. + */ + public float linearInterpolation; + } + + 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"); + } + AnimatorSet anim = new AnimatorSet(); + PendingAnimation pendingAnimation = new PendingAnimation(anim); + + int count = getChildCount(); + if (count == 0) { + return pendingAnimation; + } + + int[] oldScroll = new int[count]; + getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); + + int[] newScroll = new int[count]; + getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView); + + int maxScrollDiff = 0; + int lastPage = mIsRtl ? 0 : count - 1; + if (getChildAt(lastPage) == taskView) { + if (count > 1) { + int secondLastPage = mIsRtl ? 1 : count - 2; + maxScrollDiff = oldScroll[lastPage] - newScroll[secondLastPage]; + } + } + + boolean needsCurveUpdates = false; + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child == taskView) { + 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) { + addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), + duration, ACCEL, anim); + needsCurveUpdates = true; + } + } + } + + if (needsCurveUpdates) { + ValueAnimator va = ValueAnimator.ofFloat(0, 1); + va.addUpdateListener((a) -> updateCurveProperties()); + anim.play(va); + } + + // Add a tiny bit of translation Z, so that it draws on top of other views + if (animateTaskView) { + taskView.setTranslationZ(0.1f); + } + + mPendingAnimation = pendingAnimation; + mPendingAnimation.addEndListener((isSuccess) -> { + if (isSuccess) { + if (removeTask) { + ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id); + } + removeView(taskView); + if (getChildCount() == 0) { + onAllTasksRemoved(); + } + } + resetTaskVisuals(); + mPendingAnimation = null; + }); + return pendingAnimation; + } + + private static void addAnim(ObjectAnimator anim, long duration, + TimeInterpolator interpolator, AnimatorSet set) { + 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/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java similarity index 87% rename from quickstep/src/com/android/quickstep/TaskMenuView.java rename to quickstep/src/com/android/quickstep/views/TaskMenuView.java index 6bbcb37395..30cbcdf674 100644 --- a/quickstep/src/com/android/quickstep/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.views; import android.animation.Animator; import android.animation.AnimatorSet; @@ -32,14 +32,16 @@ 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; /** * Contains options for a recent task when long-pressing its icon. @@ -58,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; @@ -70,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 @@ -90,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); @@ -120,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); } @@ -130,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); @@ -143,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); } @@ -155,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); @@ -165,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); @@ -209,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/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java similarity index 96% rename from quickstep/src/com/android/quickstep/TaskThumbnailView.java rename to quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index 9b9c6f218d..87bb53bb61 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.views; import android.content.Context; import android.content.res.Configuration; @@ -33,9 +33,10 @@ import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; +import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskOverlayFactory.TaskOverlay; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -74,6 +75,7 @@ public class TaskThumbnailView extends View { mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius); mFadeLength = getResources().getDimension(R.dimen.task_fade_length); mOverlay = TaskOverlayFactory.get(context).createOverlay(this); + mPaint.setFilterBitmap(true); } public void bind() { @@ -151,7 +153,8 @@ public class TaskThumbnailView extends View { } else { final Configuration configuration = getContext().getApplicationContext().getResources().getConfiguration(); - final DeviceProfile profile = Launcher.getLauncher(getContext()).getDeviceProfile(); + final DeviceProfile profile = BaseActivity.fromContext(getContext()) + .getDeviceProfile(); if (configuration.orientation == mThumbnailData.orientation) { // If we are in the same orientation as the screenshot, just scale it to the // width of the task view diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java similarity index 74% rename from quickstep/src/com/android/quickstep/TaskView.java rename to quickstep/src/com/android/quickstep/views/TaskView.java index b407f75ffa..9884a4858f 100644 --- a/quickstep/src/com/android/quickstep/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -14,10 +14,7 @@ * limitations under the License. */ -package com.android.quickstep; - -import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK; -import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE; +package com.android.quickstep.views; import android.animation.TimeInterpolator; import android.app.ActivityOptions; @@ -31,10 +28,10 @@ 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.RecentsView.PageCallbacks; -import com.android.quickstep.RecentsView.ScrollState; +import com.android.quickstep.views.RecentsView.PageCallbacks; +import com.android.quickstep.views.RecentsView.ScrollState; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskCallbacks; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -47,17 +44,20 @@ import java.util.function.Consumer; */ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks { - /** Designates how "curvy" the carousel is from 0 to 1, where 0 is a straight line. */ - public static final float CURVE_FACTOR = 0.25f; - /** A circular curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */ - public static final TimeInterpolator CURVE_INTERPOLATOR - = x -> (float) (1 - Math.sqrt(1 - Math.pow(x, 2))); + /** A curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */ + private static final TimeInterpolator CURVE_INTERPOLATOR + = x -> (float) -Math.cos(x * Math.PI) / 2f + .5f; /** * The alpha of a black scrim on a page in the carousel as it leaves the screen. * In the resting position of the carousel, the adjacent pages have about half this scrim. */ - private static final float MAX_PAGE_SCRIM_ALPHA = 0.8f; + 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; @@ -96,6 +96,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback mTask = task; mSnapshotView.bind(); task.addCallback(this); + setContentDescription(task.titleDescription); } public Task getTask() { @@ -115,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); } @@ -159,38 +161,26 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback setScaleY(1f); setTranslationX(0f); setTranslationY(0f); + setTranslationZ(0); setAlpha(1f); } @Override - public int onPageScroll(ScrollState scrollState) { + public void onPageScroll(ScrollState scrollState) { float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation); - float scale = 1 - curveInterpolation * CURVE_FACTOR; - setScaleX(scale); - setScaleY(scale); - - // Make sure the biggest card (i.e. the one in front) shows on top of the adjacent ones. - setTranslationZ(scale); mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA); - float translation = - scrollState.distanceFromScreenCenter * curveInterpolation * CURVE_FACTOR; + float scale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR; + setScaleX(scale); + setScaleY(scale); + } - if (scrollState.lastScrollType == SCROLL_TYPE_WORKSPACE) { - // Make sure that the task cards do not overlap with the workspace card - float min = scrollState.halfPageWidth * (1 - scale); - if (scrollState.isRtl) { - setTranslationX(Math.min(translation, min) - scrollState.prevPageExtraWidth); - } else { - setTranslationX(Math.max(translation, -min) + scrollState.prevPageExtraWidth); - } - } else { - setTranslationX(translation); - } - scrollState.prevPageExtraWidth = 0; - return SCROLL_TYPE_TASK; + @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-v24/ic_info_shadow.xml b/res/drawable-v24/ic_setup_shadow.xml similarity index 94% rename from res/drawable-v24/ic_info_shadow.xml rename to res/drawable-v24/ic_setup_shadow.xml index 1fe2c46b4b..10aeee6e02 100644 --- a/res/drawable-v24/ic_info_shadow.xml +++ b/res/drawable-v24/ic_setup_shadow.xml @@ -15,5 +15,5 @@ --> diff --git a/res/drawable/all_apps_search_divider.xml b/res/drawable/bg_all_apps_searchbox.xml similarity index 78% rename from res/drawable/all_apps_search_divider.xml rename to res/drawable/bg_all_apps_searchbox.xml index 99905e4233..c3249279af 100644 --- a/res/drawable/all_apps_search_divider.xml +++ b/res/drawable/bg_all_apps_searchbox.xml @@ -1,5 +1,5 @@ - - - - + + + \ No newline at end of file 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/drawable/ic_setting.xml b/res/drawable/ic_setting.xml index 08eba253dc..33cd6ce34a 100644 --- a/res/drawable/ic_setting.xml +++ b/res/drawable/ic_setting.xml @@ -17,9 +17,10 @@ Copyright (C) 2016 The Android Open Source Project android:width="@dimen/options_menu_icon_size" android:height="@dimen/options_menu_icon_size" android:viewportWidth="48.0" - android:viewportHeight="48.0"> + android:viewportHeight="48.0" + android:tint="?android:attr/textColorPrimary" > - - + layout="@layout/search_container_all_apps"/> - + \ 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" > + android:focusable="true" /> 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/deep_shortcut.xml b/res/layout/deep_shortcut.xml index 4a3db1f136..4a2ad42254 100644 --- a/res/layout/deep_shortcut.xml +++ b/res/layout/deep_shortcut.xml @@ -35,7 +35,6 @@ android:textColor="?android:attr/textColorPrimary" android:fontFamily="sans-serif" launcher:layoutHorizontal="true" - launcher:deferShadowGeneration="true" launcher:iconDisplay="shortcut_popup" launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" /> diff --git a/res/layout/drop_target_bar.xml b/res/layout/drop_target_bar.xml index d376bcf09f..2f21c605a5 100644 --- a/res/layout/drop_target_bar.xml +++ b/res/layout/drop_target_bar.xml @@ -32,18 +32,8 @@ android:gravity="center" android:text="@string/remove_drop_target_label" /> - - - - + + + - - - - - + + + diff --git a/res/layout/widgets_bottom_sheet_scrim.xml b/res/layout/launcher_preference.xml similarity index 65% rename from res/layout/widgets_bottom_sheet_scrim.xml rename to res/layout/launcher_preference.xml index 6c6626c1d2..ed0ea7c0e2 100644 --- a/res/layout/widgets_bottom_sheet_scrim.xml +++ b/res/layout/launcher_preference.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + android:cacheColorHint="@android:color/transparent" + android:clipToPadding="false" + android:drawSelectorOnTop="false" + android:orientation="vertical" + android:scrollbarAlwaysDrawVerticalTrack="true" + android:scrollbarStyle="outsideOverlay" /> \ No newline at end of file diff --git a/quickstep/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml similarity index 97% rename from quickstep/res/layout/longpress_options_menu.xml rename to res/layout/longpress_options_menu.xml index 9cf0fcfa44..71d117af86 100644 --- a/quickstep/res/layout/longpress_options_menu.xml +++ b/res/layout/longpress_options_menu.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - \ No newline at end of file + \ No newline at end of file diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml index c795b81fee..bdd5d23476 100644 --- a/res/layout/overview_panel.xml +++ b/res/layout/overview_panel.xml @@ -14,61 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - - - - \ No newline at end of file + android:layout_width="0dp" + android:layout_height="0dp" /> \ No newline at end of file diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml index fc07002cb5..14d7b53b65 100644 --- a/res/layout/search_container_all_apps.xml +++ b/res/layout/search_container_all_apps.xml @@ -17,53 +17,23 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@id/search_container_all_apps" android:layout_width="match_parent" - android:layout_height="@dimen/all_apps_search_bar_height" - android:layout_gravity="center|top" - android:layout_marginBottom="-8dp" - android:gravity="center|bottom" - android:paddingLeft="@dimen/dynamic_grid_edge_margin" - android:paddingRight="@dimen/dynamic_grid_edge_margin" - android:saveEnabled="false" > - - - - - - - - - \ No newline at end of file + android:layout_height="@dimen/all_apps_search_bar_field_height" + android:layout_centerHorizontal="true" + android:layout_gravity="top|center_horizontal" + android:layout_marginTop="8dp" + android:background="@drawable/bg_all_apps_searchbox" + android:elevation="1dp" + android:focusableInTouchMode="true" + android:gravity="center" + android:hint="@string/all_apps_search_bar_hint" + android:imeOptions="actionSearch|flagNoExtractUi" + android:inputType="text|textNoSuggestions|textCapWords" + android:maxLines="1" + android:padding="8dp" + android:saveEnabled="false" + android:scrollHorizontally="true" + android:singleLine="true" + android:textColor="?android:attr/textColorSecondary" + android:textColorHint="@drawable/all_apps_search_hint" + android:textSize="16sp" + android:translationY="24dp" /> \ No newline at end of file diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml index 1888e2254d..04f3d027dc 100644 --- a/res/layout/system_shortcut.xml +++ b/res/layout/system_shortcut.xml @@ -34,7 +34,6 @@ android:fontFamily="sans-serif" launcher:iconDisplay="shortcut_popup" launcher:layoutHorizontal="true" - launcher:deferShadowGeneration="true" android:focusable="false" /> - - + android:clipToPadding="false" /> - - \ 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 4cd03ce059..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" @@ -42,7 +41,6 @@ android:textColor="?android:attr/textColorPrimary" android:textSize="16sp" android:textAlignment="viewStart" - launcher:deferShadowGeneration="true" launcher:iconDisplay="widget_section" launcher:iconSizeOverride="@dimen/widget_section_icon_size" launcher:layoutHorizontal="true" /> 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,7 +138,9 @@ "Werk" "Werkprofiel" "Kry werkprogramme hier" - "Elke werkprogram het \'n oranje kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou Tuisskerm vir makliker toegang." + "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-am/strings.xml b/res/values-am/strings.xml index 67c26e79a0..07fe5ef7b1 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -137,7 +137,8 @@ "ሥራ" "የሥራ መገለጫ" "የስራ መተግበሪያዎችን እዚህ ያግኙ" - "እያንዳንዱ የሥራ መተግበሪያ ብርቱካናማ ባጅ አለው እና በእርስዎ ድርጅት በኩል ደህንነቱ ተጠብቋል። ለቀለለ መዳረሻ መተግበሪያዎችን ወደ የእርስዎ መነሻ ማያ ገጽ ያንቀሳቅሱ።" + + "በእርስዎ ድርጅት የሚተዳደር" "ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 1b1d7ca6ba..95508e1212 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -141,7 +141,8 @@ "للعمل" "الملف الشخصي للعمل" "البحث عن تطبيقات العمل هنا" - "يحتوي كل تطبيق للعمل على شارة برتقالية اللون ويظل تحت حماية مؤسستك. يمكنك نقل التطبيقات إلى شاشتك الرئيسية لتسهيل الوصول." + + "ملف شخصي للعمل تديره مؤسستك" "الإشعارات والتطبيقات متوقفة." diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 00f71d03c3..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,7 +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." + "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-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 0ee8b3ac49..4b577bad19 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -138,7 +138,8 @@ "Poslovne" "Profil za Work" "Pronađite poslovne aplikacije ovde" - "Svaka poslovna aplikacija ima narandžastu značku i štiti je vaša organizacija. Premestite aplikacije na početni ekran da biste im lakše pristupali." + + "Ovim upravlja organizacija" "Obaveštenja i aplikacije su isključeni" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 2f5eac7ec0..155ac3cdfe 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -139,7 +139,8 @@ "Праца" "Працоўны профіль" "Знайдзіце працоўныя праграмы тут" - "Кожная працоўная праграма мае аранжавы значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на свой Галоўны экран." + + "Пад кіраваннем вашай арганізацыі" "Апавяшчэнні і праграмы выключаны" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 94b7f13840..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,7 +138,9 @@ "Служебни" "Служебен потребителски профил" "Тук можете да намерите служебните приложения" - "Всяко служебно приложение има оранжева значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран." + "Всяко служебно приложение има значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран." "Управлява се от организацията ви" "Известията и приложенията са изключени" + "Затваряне" + "Затворено" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index d9c6033b8a..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,7 +138,11 @@ "অফিস" "অফিসের প্রোফাইল" "এখানে কাজের অ্যাপ্সগুলি খুঁজুন" - "প্রতিটি কাজের অ্যাপে একটি করে কমলা ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।" + "প্রতিটি কাজের অ্যাপে একটি করে ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।" "আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত" "বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে" + + + + diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 84b2b78189..a96abdee8e 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -41,7 +41,7 @@ "Pretraži više aplikacija" "Obavještenja" "Na ovom početnom ekranu nema više prostora." - "Nema više prostora u ladici Favoriti" + "Nema više prostora u ladici Omiljeno" "Spisak aplikacija" "Početna" "Ukloni" @@ -112,7 +112,7 @@ "Premjesti stavku" "Pomjeri stavku u red %1$s kolonu %2$s" "Pomjeri stavku na poziciju %1$s" - "Pomjeri stavku na poziciju %1$s među favoritima" + "Pomjeri stavku na poziciju %1$s među omiljenim" "Stavka je premještena" "Dodaj u folder: %1$s" "Dodaj u folder sa aplikacijom %1$s" @@ -138,7 +138,8 @@ "Poslovne" "Radni profil" "Pronađite poslovne aplikacije ovdje" - "Svaka poslovna aplikacija ima narandžastu značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa." + + "Upravlja vaša organizacija" "Notifikacije i aplikacije su isključene" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 1ca68eb8b6..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,7 +138,9 @@ "Feina" "Perfil professional" "Cerca aplicacions per a la feina aquí" - "Totes les aplicacions per a la feina tenen una insígnia taronja 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." + "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-cs/strings.xml b/res/values-cs/strings.xml index e5fde42cba..608d297ff5 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -139,7 +139,8 @@ "Pracovní" "Pracovní profil" "Zde naleznete pracovní aplikace" - "Každý pracovní profil má oranžový odznak a je zabezpečen vaší organizací. Aplikace si můžete pro jednoduchost přesunout na plochu." + + "Spravováno vaší organizací" "Oznámení a aplikace jsou vypnuty" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 97d8a265e5..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,7 +137,7 @@ "Arbejde" "Arbejdsprofil" "Find arbejdsapps her" - "Alle arbejdsapps har et orange badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem." + "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-de/strings.xml b/res/values-de/strings.xml index 96acfba80d..5cc1451b3b 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -137,7 +137,8 @@ "Geschäftlich" "Arbeitsprofil" "Hier findest du Apps für die Arbeit" - "Jede App für die Arbeit hat ein orangefarbenes Logo. Deine Organisation kümmert sich um den entsprechenden Schutz. Damit du leichter auf Apps zugreifen kannst, verschiebe sie auf deinen Startbildschirm." + + "Wird von deiner Organisation verwaltet" "Benachrichtigungen und Apps sind deaktiviert" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 9328c5f311..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,7 +138,9 @@ "Εργασίας" "Προφίλ εργασίας" "Βρείτε όλες τις εφαρμογές εργασίας εδώ" - "Κάθε εφαρμογή εργασίας φέρει ένα πορτοκαλί σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση." + "Κάθε εφαρμογή εργασίας φέρει ένα σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση." "Διαχειριζόμενο από τον οργανισμό σας" "Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες" + "Κλείσιμο" + "Κλειστή" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index d7ab7d252b..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,7 +138,9 @@ "Work" "Work profile" "Find work apps here" - "Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access." + "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 d7ab7d252b..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,7 +138,9 @@ "Work" "Work profile" "Find work apps here" - "Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access." + "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 d7ab7d252b..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,7 +138,9 @@ "Work" "Work profile" "Find work apps here" - "Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access." + "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-rUS/strings.xml b/res/values-es-rUS/strings.xml index 11cfa2a457..23bf97d5e9 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -137,7 +137,8 @@ "Laborales" "Perfil de trabajo" "Apps de trabajo" - "Cada app de trabajo tiene una insignia naranja y está protegida por tu organización. Mueve las apps a la pantalla principal para acceder a ellas con mayor facilidad." + + "Administrado por tu organización" "Las notificaciones y las apps están desactivadas" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 39da94c1f5..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,7 +138,9 @@ "Trabajo" "Perfil de trabajo" "Aplicaciones de trabajo" - "Cada aplicación de trabajo tiene una insignia naranja y está protegida por tu organización. Mueve las aplicaciones a la pantalla de inicio para acceder a ellas más fácilmente." + "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 68a0429ecb..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,7 +138,9 @@ "Töö" "Tööprofiil" "Töörakendused leiate siit" - "Igal töörakendusel on oranž märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda." + "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 27a56dd11c..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,7 +138,9 @@ "Lanekoak" "Laneko profila" "Hemen dituzu laneko aplikazioak" - "Laneko aplikazio bakoitzak bereizgarri laranja bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara." + "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-fa/strings.xml b/res/values-fa/strings.xml index 128af9032f..5528119723 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -137,7 +137,8 @@ "محل کار" "نمایه کاری" "اینجا برنامه‌های کاری را پیدا کنید" - "همه برنامه‌های کاری نشان نارنجی‌رنگی دارند و توسط سازمان شما امن نگه داشته می‌شود. برنامه‌های کاری را برای دسترسی آسان‌تر به صفحه اصلی انتقال دهید." + + "توسط سازمانتان مدیریت می‌شود" "اعلان‌ها و برنامه‌ها خاموش هستند" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index f5b08179b9..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,7 +138,9 @@ "Työsovellukset" "Työprofiili" "Etsi työsovelluksia tästä" - "Kaikissa työsovelluksissa on oranssi merkki ja ne ovat organisaatiosi suojaamia. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi." + "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-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 40935354d7..c6961805fd 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -137,7 +137,8 @@ "Travail" "Profil professionnel" "Trouvez ici des applications professionnelles" - "Chaque application professionnelle comporte un badge orange, ce qui signifie qu\'elle est sécurisée par votre organisation. Vous pouvez déplacer vos applications vers l\'écran d\'accueil afin d\'y accéder plus facilement." + + "Géré par votre organisation" "Les notifications et les applications sont désactivées" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 520ad78a4f..e0997fc85a 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -137,7 +137,8 @@ "Professionnelles" "Profil professionnel" "Retrouvez ici vos applications professionnelles" - "Les applications professionnelles sont accompagnées d\'un badge orange et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement." + + "Géré par votre organisation" "Les notifications et les applications sont désactivées" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 9a842b1902..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,7 +138,9 @@ "Traballo" "Perfil de traballo" "Buscar aplicacións do traballo aquí" - "As aplicacións do traballo teñen unha insignia laranxa 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." + "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 e030659847..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,7 +138,11 @@ "કાર્યાલયની ઍપ" "કાર્યાલયની પ્રોફાઇલ" "કાર્ય ઍપને અહીંથી મેળવો" - "દરેક કાર્ય ઍપ પાસે એક નારંગી બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો." + "દરેક કાર્ય ઍપ પાસે એક બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો." "તમારી સંસ્થા દ્વારા મેનેજ કરેલ" "નોટિફિકેશન અને ઍપ બંધ છે" + + + + diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index aa1e8dd9f6..15a4deb945 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -137,7 +137,8 @@ "काम से जुड़े ऐप" "कार्य प्रोफ़ाइल" "काम से जुड़े सभी ऐप्लिकेशन यहां पाएं" - "काम से जुड़े हर ऐप्लिकेशन पर एक नारंगी रंग का बैज (निशान) होता है जिसकी सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं." + + "आपका संगठन प्रबंधित कर रहा है" "सूचनाएं और ऐप्लिकेशन बंद हैं" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index ec3c6b2abc..d28c108b1b 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -138,7 +138,8 @@ "Posao" "Radni profil" "Ovdje možete pronaći radne aplikacije" - "Svaka radna aplikacija ima narančastu značku i štiti je vaša organizacija. Premjestite aplikacije na početni zaslon radi lakšeg pristupa." + + "Pod upravljanjem vaše organizacije" "Obavijesti i aplikacije isključeni su" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 697153a222..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,7 +138,9 @@ "Munkahelyi" "Munkaprofil" "Itt kereshet munkahelyi alkalmazásokat" - "Az egyes munkahelyi alkalmazásokon narancs 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." + "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-hy/strings.xml b/res/values-hy/strings.xml index 02a561f9ab..e233a4404a 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -137,7 +137,8 @@ "Աշխատանքային" "Աշխատանքային պրոֆիլ" "Գտեք աշխատանքային հավելվածներ այստեղ" - "Աշխատանքային հավելվածները նշված են նարնջագույն նշանով, նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։" + + "Կառավարվում է ձեր կազմակերպության կողմից" "Ծանուցումներն ու հավելվածներն անջատված են" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index d115c73776..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,7 +138,9 @@ "Kantor" "Profil kerja" "Temukan aplikasi kerja di sini" - "Setiap aplikasi kerja memiliki badge oranye dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses." + "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-is/strings.xml b/res/values-is/strings.xml index ba9a47d85b..1e89142cb1 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -137,7 +137,8 @@ "Vinna" "Vinnusnið" "Hér finnurðu vinnuforrit" - "Öll vinnuforrit eru með appelsínugulu merki og fyrirtækið þitt tryggir öryggi þeirra. Færðu forrit yfir á heimaskjáinn fyrir auðveldari aðgang að þeim." + + "Stjórnað af fyrirtækinu þínu" "Slökkt er á tilkynningum og forritum" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 6c98d3e1b8..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,7 +138,9 @@ "Lavoro" "Profilo di lavoro" "Qui puoi trovare le tue app di lavoro" - "Ogni app di lavoro è contrassegnata da un badge arancione e viene tenuta al sicuro dalla tua organizzazione. Sposta le app nella schermata Home per accedervi più facilmente." + "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-iw/strings.xml b/res/values-iw/strings.xml index 43c8520877..961bd01117 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -139,7 +139,8 @@ "עבודה" "פרופיל עבודה" "ניתן למצוא כאן את אפליקציות העבודה" - "לכל אפליקציית עבודה יש תג כתום ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן." + + "מנוהל בידי הארגון" "הודעות ואפליקציות כבויות" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 36f3c5e95e..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,7 +138,9 @@ "仕事用" "仕事用プロファイル" "ここには仕事用アプリが表示されます" - "仕事用アプリにはオレンジのバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。" + "仕事用アプリにはバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。" "組織によって管理されています" "通知とアプリは OFF です" + "閉じる" + "終了" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index b6d78778f9..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,7 +138,9 @@ "სამსახური" "სამსახურის პროფილი" "აქ თავმოყრილია სამსახურის აპები" - "სამსახურის აპები მონიშნულია სტაფილოსფერი ბეჯით, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ." + "სამსახურის თითოეულ აპს აქვს ბეჯი, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ." "იმართება თქვენი ორგანიზაციის მიერ" "შეტყობინებები და აპები გამორთულია" + "დახურვა" + "დახურული" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 6c4deec4b4..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,7 +138,9 @@ "Жұмыс" "Жұмыс профилі" "Жұмыс қолданбалары осы жерде берілген" - "Әрбір жұмыс қолданбасында қызғылт сары танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады." + "Әрбір жұмыс қолданбасында танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады." "Ұйым арқылы басқарылады" "Хабарландырулар мен қолданбалар өшірулі" + "Жабу" + "Жабық" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index d588bc19a7..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,7 +138,9 @@ "ការងារ" "កម្រងព័ត៌មានការងារ" "ស្វែង​រក​កម្មវិធី​ការងារ​នៅទីនេះ" - "កម្មវិធី​ការងារ​នីមួយៗ​មាន​ស្លាក​​ពណ៌​ទឹកក្រូច និងត្រូវ​បានរក្សាទុក​យ៉ាងមានសុវត្ថិភាព​ដោយស្ថាប័ន​របស់អ្នក។ សូម​ផ្លាស់ទី​កម្មវិធី​ទៅកាន់​អេក្រង់​ដើម​របស់អ្នក​ ដើម្បី​ងាយស្រួល​ចូលប្រើជាងមុន។" + "កម្មវិធី​ការងារ​នីមួយៗ​មាន​ស្លាកមួយ និងត្រូវ​បានរក្សាទុក​យ៉ាងមានសុវត្ថិភាព​ដោយស្ថាប័ន​របស់អ្នក។ សូម​ផ្លាស់ទី​កម្មវិធី​ទៅកាន់​អេក្រង់​ដើម​របស់អ្នក​ ដើម្បី​ងាយស្រួល​ចូលប្រើជាងមុន។" "គ្រប់គ្រងដោយ​ស្ថាប័ន​របស់អ្នក" "ការជូនដំណឹង​ និងកម្មវិធី​ត្រូវបានបិទ" + "បិទ" + "បានបិទ" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index a8f97847c8..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,7 +138,11 @@ "ಕೆಲಸ" "ಕೆಲಸದ ಪ್ರೊಫೈಲ್" "ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಹುಡುಕಿ" - "ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಕಿತ್ತಳೆ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸರಿಸಿ." + "ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸರಿಸಿ." "ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ" "ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಆಫ್ ಆಗಿವೆ" + + + + diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 0ebbbe717b..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,7 +138,9 @@ "직장" "직장 프로필" "여기에서 업무용 앱 찾기" - "각 업무용 앱에는 주황색 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요." + "각 업무용 앱에는 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요." "조직에서 관리" "알림 및 앱 사용 중지됨" + "닫기" + "종료됨" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 05652fc143..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,7 +138,9 @@ "Жумуш колдонмолору" "Жумуш профили" "Жумуш колдонмолорун бул жерден таап алыңыз" - "Ар бир жумуш колдонмосунун кызгылт сары бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз." + "Ар бир жумуш колдонмосунун бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз." "Уюмуңуз тарабынан башкарылат" "Билдирүүлөр жана колдонмолор өчүрүлгөн" + "Жабуу" + "Жабык" diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml index 40ffffe128..6cf23ad10a 100644 --- a/res/values-land/dimens.xml +++ b/res/values-land/dimens.xml @@ -25,7 +25,6 @@ 24dp - 120dp 48dp 4dp diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index e446e7c50e..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,7 +138,9 @@ "ວຽກ" "ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ" "ຊອກຫາແອັບວຽກຢູ່ບ່ອນນີ້" - "ແຕ່ລະແອັບວຽກຈະມີປ້າຍສີສົ້ມ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້." + "ແຕ່ລະແອັບວຽກຈະມີປ້າຍ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້." "ຈັດການໂດຍອົງກອນຂອງທ່ານ" "ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້" + "ປິດ" + "ປິດແລ້ວ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 2e37e98ae9..3e985cb094 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -139,7 +139,8 @@ "Darbo" "Darbo profilis" "Darbo programas rasite čia" - "Kiekvienai darbo programai priskirtas oranžinis ženklelis, o tokių programų sauga rūpinasi jūsų organizacija. Perkelkite darbo programas į pagrindinį ekraną, kad galėtumėte lengviau jas pasiekti." + + "Tvarko jūsų organizacija" "Programos ir pranešimai išjungti" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 3b96ffa048..6a9d15791b 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -138,7 +138,8 @@ "Darba lietotnes" "Darba profils" "Meklējiet darba lietotnes šeit" - "Katrai darba lietotnei ir oranža emblēma, un jūsu organizācija aizsargā šīs lietotnes. Ērtākai piekļuvei pārvietojiet darba lietotnes uz sākuma ekrānu." + + "Pārvalda jūsu organizācija" "Paziņojumi un lietotnes ir izslēgtas" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 3a0fc2b633..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,7 +138,9 @@ "За работа" "Работен профил" "Најдете апликации за работа тука" - "Секоја апликација за работа има портокалова значка и е обезбедена од вашата организација. За полесен пристап, апликациите за работа преместете ги на почетниот екран." + "Секоја апликација за работа има значка, а организацијата се грижи за нејзината безбедност. За полесен пристап, преместете ги апликациите на почетниот екран." "Управувано од вашата организација" "Известувањата и апликациите се исклучени" + "Затвори" + "Затворено" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 84f769b494..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,7 +138,11 @@ "ജോലി" "ഔദ്യോഗിക പ്രൊഫൈൽ" "ഔദ്യോഗിക ആപ്പുകൾ ഇവിടെ കണ്ടെത്തുക" - "എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഓറഞ്ച് നിറത്തിലുള്ള ഒരു ബാഡ്‌ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക." + "എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഒരു ബാഡ്‌ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക." "നിങ്ങളുടെ സ്ഥാപനം നിയന്ത്രിക്കുന്നത്" "അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്" + + + + diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 5e91a4601a..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,7 +138,9 @@ "Ажил" "Ажлын профайл" "Ажлын аппыг эндээс олно уу" - "Ажлын апп тус бүр улбар шар тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбараар хандахын тулд тэдгээрийг Үндсэн Нүүрэнд зөөнө үү." + "Ажлын апп тус бүр тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбар хандахын тулд тэдгээрийг Үндсэн нүүр хэсэгт зөөнө үү." "Танай байгууллагаас удирддаг" "Мэдэгдэл, апп унтраалттай байна" + "Хаах" + "Хаасан" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 94b75d7885..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,7 +138,11 @@ "कार्यालय" "कार्य प्रोफाइल" "कामाची अ‍ॅप्स येथे मिळवा" - "प्रत्येक कार्य अ‍ॅपमध्ये नारिंगी बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अ‍ॅक्सेससाठी अ‍ॅप्स तुमच्या होम स्क्रीनवर हलवा." + "प्रत्येक कार्य अ‍ॅपला एक बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अ‍ॅक्सेससाठी अ‍ॅप्स तुमच्या होम स्क्रीनवर हलवा." "तुमच्या संस्थेकडून व्यवस्थापित" "सूचना आणि अॅप्स बंद आहेत" + + + + diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 6e19421768..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,7 +138,9 @@ "Kerja" "Profil kerja" "Temui apl kerja di sini" - "Setiap apl kerja terdapat lencana berwarna oren dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebh mudah." + "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 3cf9208dfe..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,7 +138,9 @@ "အလုပ်" "အလုပ်ပရိုဖိုင်" "အလုပ်အက်ပ်များကို ဤနေရာတွင်ရှာဖွေပါ" - "အလုပ်အက်ပ်တိုင်းတွင် လိမ္မော်ရောင်တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။" + "အလုပ်အက်ပ်တိုင်းတွင် တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။" "သင်၏ အဖွဲ့အစည်းက စီမံခန့်ခွဲထားပါသည်" "အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်" + "ပိတ်ရန်" + "ပိတ်ထားသည်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 2bdd6ce40d..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,7 +138,9 @@ "Jobb" "Jobbprofil" "Finn jobbapper her" - "Alle jobbapper har et oransje merke og sikres av organisasjonen din. Flytt apper til startskjermen for å gjøre det enklere å finne dem." + "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 d15e8b220d..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,7 +138,11 @@ "कार्यसम्बन्धी" "कार्य प्रोफाइल" "कार्यसम्बन्धी अनुप्रयोगहरू यहाँ प्राप्त गर्नुहोस्" - "कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा सुन्तला रङको ब्याज छ र यसलाई तपाईंको संगठनद्वारा सुरक्षित राखिएको छ । अझ सजिलो गरी पहुँच राख्नका लागि कार्यसम्बन्धी अनुप्रयोगहरूलाई तपाईंको गृहस्क्रिनमा सार्नुहोस्‌।" + "कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा ब्याज छ र तपाईंको संगठनले यसलाई सुरक्षित राखेको छ । अझ सजिलो गरी पहुँच राख्नका लागि अनुप्रयोगहरूलाई आफ्नो गृहस्क्रिनमा सार्नुहोस्‌।" "तपाईंको सङ्गठनले व्यवस्थापन गरेको" "सूचना र अनुप्रयोगहरू निष्क्रिय छन्‌" + + + + diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 38c24a5a8a..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,7 +138,9 @@ "Werk" "Werkprofiel" "Zoek hier naar werk-apps" - "Elke werk-app heeft een oranje badge en wordt beveiligd door je organisatie. Verplaats apps naar je startscherm om gemakkelijker toegang te krijgen." + "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 6b6c929364..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,7 +138,11 @@ "ਕਾਰਜ-ਸਥਾਨ" "ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ" "ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ ਇੱਥੇ ਲੱਭੋ" - "ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਸੰਤਰੀ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਰਾਹੀਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।" + "ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।" "ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ" "ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ" + + + + diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index c90ba1e231..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,7 +136,7 @@ "Praca" "Profil służbowy" "Aplikacje do pracy" - "Każda aplikacja do pracy ma pomarańczową plakietkę, a o jej bezpieczeństwo dba Twoja organizacja. Aplikacje można przenieść na ekran główny, by były łatwiej dostępne." + "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 c370a8e0ff..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,7 +138,9 @@ "Trabalho" "Perfil de trabalho" "Encontrar as aplicações de trabalho aqui" - "Cada aplicação de trabalho apresenta um emblema laranja, pelo que a sua entidade a mantém em segurança. Pode mover as aplicações para o ecrã principal para facilitar o acesso." + "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-pt/strings.xml b/res/values-pt/strings.xml index 0686ae796c..48e37efb64 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -137,7 +137,8 @@ "Comerciais" "Perfil de trabalho" "Localizar apps de trabalho aqui" - "Cada app de trabalho tem um selo laranja e é mantido em segurança pela sua organização. Mova os apps para sua tela inicial para facilitar o acesso." + + "Gerenciados pela sua organização" "As notificações e os apps estão desativados" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 651b264a77..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,7 +139,9 @@ "Profesionale" "Profil de serviciu" "Găsiți aplicații de serviciu aici" - "Fiecare aplicație de serviciu are o insignă portocalie și este păstrată în siguranță de organizația dvs. Mutați aplicațiile de serviciu pe ecranul de pornire pentru acces mai ușor." + "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-ru/strings.xml b/res/values-ru/strings.xml index 9f43c56e1d..10490f38b5 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -67,7 +67,7 @@ В приложении \"%1$s\" %2$d уведомления "Стр. %1$d из %2$d" - "Главные экран %1$d из %2$d" + "Главный экран %1$d из %2$d" "Новый экран" "Папка открыта, %1$d x %2$d" "Нажмите, чтобы закрыть папку" @@ -139,7 +139,8 @@ "Рабочие" "Рабочий профиль" "Приложения для работы" - "Рабочие приложения отмечены оранжевым значком, который подтверждает, что ваша организация гарантирует их безопасность. Для удобства перенесите эти приложения на главный экран." + + "Управляется вашей организацией" "Уведомления и приложения отключены." diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index bbcc25781f..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,7 +138,9 @@ "කාර්යාලය" "කාර්යාල පැතිකඩ" "මෙහි කාර්යාල යෙදුම් සොයා ගන්න" - "සෑම කාර්යාල යෙදුමකම තැඹිලි ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය විසින් සුරක්ෂිතව තබා ගනී. වඩා පහසුවෙන් පිවිසීමට යෙදුම් ඔබගේ මුල් පිටු තිරය වෙත ගෙන යන්න." + "සෑම කාර්යාල යෙදුමකම ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය මගින් සුරක්ෂිතව තබා ගනී. වඩාත් පහසු ප්‍රවේශයකට යෙදුම් ඔබේ මුල් පිටු තිරය වෙත ගෙන යන්න." "ඔබේ සංවිධානය විසින් කළමනාකරණය කරනු ලැබේ" "දැනුම්දීම් සහ යෙදුම් ක්‍රියාවිරහිතයි" + "වසන්න" + "වසා ඇත" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index a3bc4ec577..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,7 +140,9 @@ "Pracovné" "Pracovný profil" "Tu nájdete pracovné aplikácie" - "Všetky pracovné aplikácie majú oranžový š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." + "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 337cb2de07..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,7 +140,9 @@ "Služba" "Delovni profil" "Tukaj poiščite delovne aplikacije" - "Vsaka delovna aplikacija ima oranžno značko. Za varnost teh aplikacij skrbi vaša organizacija. Za preprostejši dostop premaknite aplikacije na začetni zaslon." + "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 e79f0d448b..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,7 +138,9 @@ "Punë" "Profili i punës" "Gjej këtu aplikacionet e punës" - "Secili aplikacion pune ka një distinktiv portokalli dhe mbahet i sigurt nga organizata jote. Zhvendosi aplikacionet e punës në ekranin tënd kryesor për qasje më të lehtë." + "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-sr/strings.xml b/res/values-sr/strings.xml index 7d5e28df03..c190e758e1 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -138,7 +138,8 @@ "Пословне" "Профил за Work" "Пронађите пословне апликације овде" - "Свака пословна апликација има наранџасту значку и штити је ваша организација. Преместите апликације на почетни екран да бисте им лакше приступали." + + "Овим управља организација" "Обавештења и апликације су искључени" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index c886780e83..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,7 +138,9 @@ "Arbete" "Jobbprofil" "Här hittar du jobbappar" - "Alla jobbappar har ett orange märke och organisationen ser till att de är skyddade. Flytta apparna till startskärmen så kommer du åt dem lättare." + "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 8925c8935b..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,7 +140,9 @@ "Kazini" "Wasifu wa kazini" "Pata programu za kazi hapa" - "Kila programu ya kazi ina beji ya rangi ya machungwa na hulindwa na shirika lako. Hamishia programu kwenye skrini yako ya kwanza ili uzifikie kwa urahisi." + "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-sw720dp/styles.xml b/res/values-sw720dp/styles.xml index 72894dcf37..f322e9f31b 100644 --- a/res/values-sw720dp/styles.xml +++ b/res/values-sw720dp/styles.xml @@ -26,7 +26,6 @@ true true ?android:attr/textColorSecondary - @layout/search_container_all_apps diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index fc68b82869..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,7 +138,11 @@ "பணி" "பணி விவரம்" "பணி ஆப்ஸை இங்கு காணலாம்" - "ஒவ்வொரு பணிப் பயன்பாடும் ஆரஞ்சு நிற பேட்ஜைக் கொண்டிருக்கும். இவை, உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்." + "ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்." "உங்கள் நிறுவனம் நிர்வகிக்கிறது" "ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன" + + + + diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index fb8a71b1e3..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,7 +138,11 @@ "కార్యాలయం" "కార్యాలయ ప్రొఫైల్" "కార్యాలయ యాప్‌లను ఇక్కడ కనుగొనండి" - "ప్రతి కార్యాలయ యాప్‌కు నారింజ బ్యాడ్జ్‌ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్‌లను మీ హోమ్ స్క్రీన్‌కి తరలించండి." + "ప్రతి కార్యాలయ యాప్‌కు బ్యాడ్జ్‌ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్‌లను మీ హోమ్ స్క్రీన్‌కి తరలించండి." "మీ సంస్థ ద్వారా నిర్వహించబడతాయి" "నోటిఫికేషన్‌లు మరియు యాప్‌లు ఆఫ్ చేయబడ్డాయి" + + + + diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 6f2bc710a6..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,7 +138,9 @@ "งาน" "โปรไฟล์งาน" "หาแอปงานที่นี่" - "แอปงานแต่ละแอปมีป้ายสีส้มและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น" + "แอปงานแต่ละแอปมีป้ายและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น" "จัดการโดยองค์กร" "ปิดการแจ้งเตือนและแอปอยู่" + "ปิด" + "ปิด" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 5aad29a652..d0a820d917 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -137,7 +137,8 @@ "Trabaho" "Profile sa trabaho" "Maghanap ng mga app para sa trabaho rito" - "Ang bawat app para sa trabaho ay may orange na badge at pinapanatiling ligtas ng iyong organisasyon. Ilipat ang mga app sa iyong Home screen para mas madaling ma-access." + + "Pinamamahalaan ng iyong organisasyon" "Naka-off ang mga notification at app" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index a0ebe6f050..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,7 +138,9 @@ "İş" "İş profili" "İş uygulamalarını burada bulabilirsiniz" - "Her iş uygulamasında, uygulama güvenliğinin kuruluşunuz tarafından sağlandığını gösteren turuncu bir rozet bulunur. Uygulamaları daha kolay erişim için Ana ekranınıza taşıyın." + "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-uk/strings.xml b/res/values-uk/strings.xml index ffced576bc..095ff5ba30 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -139,7 +139,8 @@ "Робочі додатки" "Робочий профіль" "Робочі додатки містяться тут" - "Кожний робочий додаток має оранжевий значок. Його захищає організація. Для швидкого доступу перенесіть додатки на головний екран." + + "Профілем керує ваша організація" "Сповіщення та додатки вимкнено" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index cd657383d9..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,7 +138,11 @@ "دفتری" "دفتری پروفائل" "یہاں دفتری ایپس تلاش کریں" - "ہر دفتری ایپ میں نارنجی بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔" + "ہر دفتری ایپ میں ایک بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔" "آپ کی تنظیم کے زیر انتظام" "اطلاعات اور ایپس آف ہیں" + + + + diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 200c2c0a16..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,7 +138,9 @@ "Ishchi" "Ishchi profil" "Ishga oid ilovalarni shu yerdan topish mumkin" - "Apelsinrangli nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring." + "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 a5b48922b0..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,7 +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 màu cam 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." + "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-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 71e06e7aa8..694125711d 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -137,7 +137,8 @@ "工作" "工作资料" "请在此处查找工作应用" - "每个工作应用均有一个橙色徽标,并由贵单位负责确保其安全。请将工作应用移到主屏幕,以便轻松访问。" + + "由贵单位管理" "通知和应用均已关闭" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 7f792f8063..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,7 +138,9 @@ "商務" "工作設定檔" "請在此處尋找工作應用程式" - "每個工作應用程式都有橙色徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。" + "每個工作應用程式都有一個徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。" "由您的機構管理" "通知和應用程式已關閉" + "關閉" + "已關閉" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index b95e838119..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,7 +138,9 @@ "公司" "Work 設定檔" "在這裡尋找辦公應用程式" - "每個辦公應用程式都有橘色徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。" + "每個辦公應用程式都有徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。" "由貴機構所管理" "已關閉通知和應用程式" + "關閉" + "已關閉" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 65d7d4c60d..323144bd23 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -137,7 +137,8 @@ "Umsebenzi" "Iphrofayela yomsebenzi" "Thola izinhlelo zokusebenza lapha" - "Uhlo lokusebenza ngalunye lomsebenzi linebheji ewolintshi futhi igcinwa iphephile inhlangano yakho. Hambisa izinhlelo zokusebenza esikrinini sakho sasekhaya ngokufinyelela okulula." + + "Kuphethwe inhlangano yakho" "Izaziso nezinhlelo zokusebenza kuvaliwe" diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 1351dfa847..64ca05e3c2 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -44,7 +44,6 @@ - diff --git a/res/values/config.xml b/res/values/config.xml index 065ad362ec..a40afe1138 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -116,6 +116,13 @@ + + + + + + + 150 80 @@ -124,7 +131,7 @@ - + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 1f468445c9..3a06cdd815 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -23,10 +23,6 @@ 24dp 1dp 8dp - 80dp - 120dp - 80dp - 25dp 8dp 8dp @@ -41,6 +37,7 @@ 2dp 80dp 0dp + 24dp 8dp @@ -79,7 +76,7 @@ 0dp 48dp - 60dp + 30dp 40dp 144dp 700dp @@ -99,7 +96,12 @@ 8dp - + + 56dp + + 106dp + + 8dp 16dp @@ -227,5 +229,5 @@ 70dp - 48dp + 24dp diff --git a/res/values/drawables.xml b/res/values/drawables.xml index fea17b1530..1367174b1b 100644 --- a/res/values/drawables.xml +++ b/res/values/drawables.xml @@ -14,7 +14,7 @@ limitations under the License. --> - @drawable/ic_info_no_shadow + @drawable/ic_setting @drawable/ic_remove_no_shadow @drawable/ic_uninstall_no_shadow \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 381830c5db..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. @@ -80,6 +85,9 @@ Apps list + Personal apps list + Work apps list + Home @@ -169,16 +177,8 @@ Home settings Disabled by your admin - - Overview - - Allow Home screen rotation - - When phone is rotated - - Current Display setting doesn\'t permit rotation Notification dots @@ -191,6 +191,8 @@ To show Notification Dots, turn on app notifications for %1$s Change settings + + Show notification dots Add icon to Home screen @@ -199,6 +201,8 @@ Change icon shape + + on Home screen Use system default @@ -282,15 +286,6 @@ Move to Home screen - - Move screen to left - - - Move screen to right - - - Screen moved - Resize @@ -340,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 8cc4743a32..38b5daedbf 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -25,7 +25,6 @@ true true #FF757575 - @layout/search_container_all_apps