Merge branch 'ub-launcher3-master' into pi-dev
Bug: 74794600 Test: manual test Change-Id: Id34a70abfa5de9dd741848efa3b82fae608c0df0
This commit is contained in:
@@ -87,9 +87,11 @@
|
||||
android:process=":wallpaper_chooser"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service android:name="com.android.launcher3.notification.NotificationListener"
|
||||
android:enabled="@bool/notification_badging_enabled"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<service
|
||||
android:name="com.android.launcher3.notification.NotificationListener"
|
||||
android:label="@string/icon_badging_service_title"
|
||||
android:enabled="@bool/notification_badging_enabled"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -102,6 +102,11 @@
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
# InstantAppResolver
|
||||
-keep class com.android.quickstep.InstantAppResolverImpl {
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
|
||||
*;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,13 @@
|
||||
android:restoreAnyVersion="true"
|
||||
android:supportsRtl="true" >
|
||||
|
||||
<service android:name="com.android.quickstep.TouchInteractionService"
|
||||
android:exported="true" />
|
||||
<service
|
||||
android:name="com.android.quickstep.TouchInteractionService"
|
||||
android:permission="android.permission.STATUS_BAR_SERVICE" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.QUICKSTEP_SERVICE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- STOPSHIP: Change exported to false once all the integration is complete.
|
||||
It is set to true so that the activity can be started from command line -->
|
||||
|
||||
Binary file not shown.
+14
-8
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
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.
|
||||
@@ -13,11 +13,17 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<com.android.launcher3.graphics.GradientView
|
||||
<com.android.quickstep.RecentsRootView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/gradient_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
launcher:layout_ignoreInsets="true" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.android.quickstep.FallbackRecentsView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:theme="@style/HomeScreenElementTheme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</com.android.quickstep.RecentsRootView>
|
||||
@@ -14,7 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.quickstep.RecentsView
|
||||
<com.android.quickstep.views.LauncherRecentsView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:theme="@style/HomeScreenElementTheme"
|
||||
android:layout_width="match_parent"
|
||||
@@ -24,8 +24,4 @@
|
||||
android:alpha="0.0"
|
||||
android:visibility="invisible" >
|
||||
|
||||
<com.android.launcher3.uioverrides.WorkspaceCard
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.android.quickstep.RecentsView>
|
||||
</com.android.quickstep.views.LauncherRecentsView>
|
||||
@@ -13,12 +13,12 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.quickstep.TaskView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.quickstep.views.TaskView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:elevation="4dp">
|
||||
|
||||
<com.android.quickstep.TaskThumbnailView
|
||||
<com.android.quickstep.views.TaskThumbnailView
|
||||
android:id="@+id/snapshot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -29,4 +29,4 @@
|
||||
android:layout_width="@dimen/task_thumbnail_icon_size"
|
||||
android:layout_height="@dimen/task_thumbnail_icon_size"
|
||||
android:layout_gravity="top|center_horizontal" />
|
||||
</com.android.quickstep.TaskView>
|
||||
</com.android.quickstep.views.TaskView>
|
||||
@@ -14,7 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.quickstep.TaskMenuView
|
||||
<com.android.quickstep.views.TaskMenuView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/bg_popup_item_width"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -33,4 +33,4 @@
|
||||
android:paddingTop="18dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_horizontal"/>
|
||||
</com.android.quickstep.TaskMenuView>
|
||||
</com.android.quickstep.views.TaskMenuView>
|
||||
@@ -16,24 +16,21 @@
|
||||
|
||||
<resources>
|
||||
|
||||
<dimen name="options_menu_icon_size">24dp</dimen>
|
||||
|
||||
<dimen name="task_thumbnail_top_margin">24dp</dimen>
|
||||
<dimen name="task_thumbnail_icon_size">48dp</dimen>
|
||||
<dimen name="task_menu_background_radius">12dp</dimen>
|
||||
<dimen name="task_corner_radius">2dp</dimen>
|
||||
<dimen name="task_fade_length">20dp</dimen>
|
||||
<dimen name="recents_page_spacing">10dp</dimen>
|
||||
<dimen name="recents_page_fade_length">100dp</dimen>
|
||||
|
||||
<!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
|
||||
loading full resolution screenshots. -->
|
||||
<dimen name="recents_fast_fling_velocity">600dp</dimen>
|
||||
|
||||
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
|
||||
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
|
||||
|
||||
<dimen name="workspace_overview_offset_x">-24dp</dimen>
|
||||
|
||||
<!-- TODO: This can be calculated using other resource values -->
|
||||
<dimen name="all_apps_search_box_full_height">90dp</dimen>
|
||||
|
||||
<!-- Launcher app transition -->
|
||||
<dimen name="content_trans_y">25dp</dimen>
|
||||
<dimen name="workspace_trans_y">80dp</dimen>
|
||||
|
||||
@@ -16,5 +16,7 @@
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
|
||||
|
||||
<string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
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;
|
||||
@@ -31,11 +32,13 @@ 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.util.Log;
|
||||
@@ -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;
|
||||
@@ -74,12 +81,16 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
|
||||
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.
|
||||
@@ -151,6 +162,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
@Override
|
||||
public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
|
||||
if (hasControlRemoteAppTransitionPermission()) {
|
||||
TaskView taskView = findTaskViewToLaunch(launcher, v);
|
||||
try {
|
||||
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) {
|
||||
@Override
|
||||
@@ -160,8 +172,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
// processed before the next frame.
|
||||
postAtFrontOfQueueAsynchronously(v.getHandler(), () -> {
|
||||
final boolean removeTrackingView;
|
||||
LauncherTransitionAnimator animator =
|
||||
composeRecentsLaunchAnimator(v, targets);
|
||||
LauncherTransitionAnimator animator = composeRecentsLaunchAnimator(
|
||||
taskView == null ? v : taskView, targets);
|
||||
if (animator != null) {
|
||||
// We are animating the task view directly, do not remove it after
|
||||
removeTrackingView = false;
|
||||
@@ -201,8 +213,10 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
}
|
||||
};
|
||||
|
||||
return ActivityOptionsCompat.makeRemoteAnimation(
|
||||
new RemoteAnimationAdapterCompat(runner, 500, 380));
|
||||
int duration = taskView != 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.
|
||||
@@ -211,20 +225,66 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
return getDefaultActivityLaunchOptions(launcher, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 TaskView findTaskViewToLaunch(Launcher launcher, View v) {
|
||||
if (v instanceof TaskView) {
|
||||
return (TaskView) v;
|
||||
}
|
||||
if (!launcher.isInState(LauncherState.OVERVIEW)) {
|
||||
return null;
|
||||
}
|
||||
if (v.getTag() instanceof ItemInfo) {
|
||||
ItemInfo itemInfo = (ItemInfo) v.getTag();
|
||||
ComponentName componentName = itemInfo.getTargetComponent();
|
||||
if (componentName != null) {
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
for (int i = 0; i < recentsView.getChildCount(); i++) {
|
||||
TaskView taskView = (TaskView) recentsView.getPageAt(i);
|
||||
if (recentsView.isTaskViewVisible(taskView)) {
|
||||
Task task = taskView.getTask();
|
||||
if (componentName.equals(task.key.getComponent())) {
|
||||
return taskView;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the animations for a launch from the recents list if possible.
|
||||
*/
|
||||
private LauncherTransitionAnimator composeRecentsLaunchAnimator(View v,
|
||||
RemoteAnimationTargetCompat[] targets) {
|
||||
RecentsView recentsView = mLauncher.getOverviewPanel();
|
||||
boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
|
||||
MutableBoolean skipLauncherChanges = new MutableBoolean(!launcherClosing);
|
||||
if (v instanceof TaskView) {
|
||||
// We already found a task view to launch, so use that for the animation.
|
||||
TaskView taskView = (TaskView) v;
|
||||
return new LauncherTransitionAnimator(getRecentsLauncherAnimator(recentsView, taskView),
|
||||
getRecentsWindowAnimator(taskView, skipLauncherChanges, targets));
|
||||
}
|
||||
|
||||
// It's possible that the launched view can still be resolved to a visible task view, check
|
||||
// the task id of the opening task and see if we can find a match.
|
||||
|
||||
// Ensure recents is actually visible
|
||||
if (!mLauncher.isInState(LauncherState.OVERVIEW)) {
|
||||
if (!mLauncher.getStateManager().getState().overviewUi) {
|
||||
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 +297,40 @@ 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
AnimatorListenerAdapter windowAnimEndListener;
|
||||
if (launcherClosing) {
|
||||
launcherAnim = getRecentsLauncherAnimator(recentsView, taskView);
|
||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
|
||||
mLauncher.getStateManager().reapplyState();
|
||||
}
|
||||
};
|
||||
} 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 LauncherTransitionAnimator(launcherAnim, windowAnim, skipLauncherChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,15 +345,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,11 +365,11 @@ 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());
|
||||
@@ -291,11 +377,11 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
LauncherAnimUtils.ofPropertyValuesHolder(centerTask,
|
||||
new PropertyListBuilder()
|
||||
.scale(v.getScaleX())
|
||||
.translationX(mIsRtl ? -translationX : translationX)
|
||||
.translationX(isRtl ? -translationX : translationX)
|
||||
.build());
|
||||
launcherAnimator.play(centerTaskParallaxToRight);
|
||||
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - launchedTaskIndex);
|
||||
if (otherAdjacentTaskIndex >= recentsView.getFirstTaskIndex()
|
||||
if (otherAdjacentTaskIndex >= 0
|
||||
&& otherAdjacentTaskIndex < recentsView.getPageCount()) {
|
||||
TaskView otherAdjacentTask = (TaskView) recentsView.getPageAt(
|
||||
otherAdjacentTaskIndex);
|
||||
@@ -303,7 +389,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
LauncherAnimUtils.ofPropertyValuesHolder(otherAdjacentTask,
|
||||
new PropertyListBuilder()
|
||||
.translationX(otherAdjacentTask.getTranslationX()
|
||||
+ (mIsRtl ? -translationX : translationX))
|
||||
+ (isRtl ? -translationX : translationX))
|
||||
.build());
|
||||
launcherAnimator.play(otherAdjacentTaskParallaxToRight);
|
||||
}
|
||||
@@ -314,7 +400,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 +410,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 +420,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, MutableBoolean skipLauncherChanges,
|
||||
RemoteAnimationTargetCompat[] targets) {
|
||||
Rect taskViewBounds = new Rect();
|
||||
mDragLayer.getDescendantRectRelativeToSelf(v, taskViewBounds);
|
||||
@@ -373,13 +456,15 @@ 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.value) {
|
||||
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);
|
||||
@@ -401,7 +486,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.value) {
|
||||
t.deferTransactionUntil(target.leash, surface, frameNumber);
|
||||
}
|
||||
}
|
||||
if (isFirstFrame) {
|
||||
t.show(target.leash);
|
||||
@@ -413,13 +501,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -497,26 +578,40 @@ 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
|
||||
final boolean isDeepShortcutTextView = v instanceof DeepShortcutTextView
|
||||
&& v.getParent() != null && v.getParent() instanceof DeepShortcutView;
|
||||
if (isDeepShortcutTextView) {
|
||||
// Deep shortcut views have their icon drawn in a sibling view.
|
||||
DeepShortcutView view = (DeepShortcutView) v.getParent();
|
||||
mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
|
||||
} else {
|
||||
mDragLayer.getDescendantRectRelativeToSelf(v, rect);
|
||||
}
|
||||
final int viewLocationStart = mIsRtl
|
||||
? mDeviceProfile.widthPx - rect.right
|
||||
: rect.left;
|
||||
int viewLocationTop = rect.top;
|
||||
final int viewLocationTop = rect.top;
|
||||
|
||||
if (isBubbleTextView) {
|
||||
((BubbleTextView) v).getIconBounds(rect);
|
||||
float startScale = 1f;
|
||||
if (isBubbleTextView && !isDeepShortcutTextView) {
|
||||
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());
|
||||
}
|
||||
LayoutParams lp = new LayoutParams(rect.width(), rect.height());
|
||||
lp.ignoreInsets = true;
|
||||
@@ -544,8 +639,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 +651,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);
|
||||
@@ -580,7 +671,13 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
*/
|
||||
private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
|
||||
Rect bounds = new Rect();
|
||||
if (v instanceof BubbleTextView) {
|
||||
boolean isDeepShortcutTextView = v instanceof DeepShortcutTextView
|
||||
&& v.getParent() != null && v.getParent() instanceof DeepShortcutView;
|
||||
if (isDeepShortcutTextView) {
|
||||
// Deep shortcut views have their icon drawn in a sibling view.
|
||||
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 +688,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;
|
||||
|
||||
@@ -713,7 +810,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
|
||||
postAtFrontOfQueueAsynchronously(handler, () -> {
|
||||
if ((Utilities.getPrefs(mLauncher)
|
||||
.getBoolean("pref_use_screenshot_for_swipe_up", false)
|
||||
&& mLauncher.isInState(LauncherState.OVERVIEW))
|
||||
&& mLauncher.getStateManager().getState().overviewUi)
|
||||
|| !launcherIsATargetWithMode(targets, MODE_OPENING)) {
|
||||
// We use a separate transition for Overview mode. And we can skip the
|
||||
// animation in cases where Launcher is not in the set of opening targets.
|
||||
@@ -750,7 +847,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.<RecentsView>getOverviewPanel().isRtl() ? -width : width) * 1.16f;
|
||||
|
||||
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
|
||||
|
||||
@@ -27,11 +27,20 @@ import android.animation.AnimatorSet;
|
||||
*/
|
||||
public class LauncherTransitionAnimator {
|
||||
|
||||
private final MutableBoolean mLauncherAnimCancelState;
|
||||
|
||||
private AnimatorSet mAnimatorSet;
|
||||
private Animator mLauncherAnimator;
|
||||
private Animator mWindowAnimator;
|
||||
|
||||
LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator) {
|
||||
this(launcherAnimator, windowAnimator, new MutableBoolean(false));
|
||||
}
|
||||
|
||||
|
||||
LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator,
|
||||
MutableBoolean launcherAnimCancelState) {
|
||||
mLauncherAnimCancelState = launcherAnimCancelState;
|
||||
if (launcherAnimator != null) {
|
||||
mLauncherAnimator = launcherAnimator;
|
||||
}
|
||||
@@ -50,6 +59,7 @@ public class LauncherTransitionAnimator {
|
||||
|
||||
public void cancel() {
|
||||
mAnimatorSet.cancel();
|
||||
mLauncherAnimCancelState.value = true;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
@@ -58,6 +68,7 @@ public class LauncherTransitionAnimator {
|
||||
|
||||
public void finishLauncherAnimation() {
|
||||
if (mLauncherAnimator != null) {
|
||||
mLauncherAnimCancelState.value = true;
|
||||
mLauncherAnimator.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
public class MutableBoolean {
|
||||
public boolean value;
|
||||
|
||||
public MutableBoolean(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ 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 +33,8 @@ 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_SHOW_SCRIM | FLAG_ALL_APPS_SCRIM;
|
||||
|
||||
private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
|
||||
@Override
|
||||
@@ -57,7 +59,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
|
||||
|
||||
@@ -31,13 +31,12 @@ 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;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
|
||||
class EventLogTags {
|
||||
private EventLogTags() {
|
||||
@@ -142,11 +141,6 @@ public class EdgeSwipeController extends VerticalSwipeController implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initSprings() {
|
||||
mSpringHandlers = new SpringAnimationHandler[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getShiftRange() {
|
||||
return getShiftRange(mLauncher);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
private static final boolean DEBUG_DIFFERENT_UI = false;
|
||||
|
||||
public FastOverviewState(int id) {
|
||||
super(id, QuickScrubController.QUICK_SWITCH_START_DURATION, STATE_FLAGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateTransitionEnd(Launcher launcher) {
|
||||
super.onStateTransitionEnd(launcher);
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHoseatAlpha(Launcher launcher) {
|
||||
if (DEBUG_DIFFERENT_UI) {
|
||||
return 0;
|
||||
}
|
||||
return super.getHoseatAlpha(launcher);
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,17 @@
|
||||
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 +34,26 @@ 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
|
||||
@@ -73,8 +69,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 +79,25 @@ 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};
|
||||
}
|
||||
|
||||
public static float getVerticalProgress(DeviceProfile grid, Context context) {
|
||||
if (!grid.isVerticalBarLayout()) {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
float total = grid.heightPx;
|
||||
float searchHeight = total - grid.availableHeightPx +
|
||||
context.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
|
||||
return 1 - (searchHeight / total);
|
||||
return new float[] {scale, 0, translationY};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
*/
|
||||
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 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;
|
||||
@@ -32,19 +36,13 @@ 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.userevent.nano.LauncherLogProto.ContainerType;
|
||||
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
|
||||
@@ -65,6 +63,7 @@ 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;
|
||||
|
||||
@@ -132,28 +131,21 @@ public class OverviewSwipeController extends AnimatorListenerAdapter
|
||||
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;
|
||||
mStartingTarget = LauncherLogProto.ItemType.TASK;
|
||||
} else if (isEventOverHotseat(ev)) {
|
||||
// The hotseat is being dragged
|
||||
directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
|
||||
mSwipeDownEnabled = false;
|
||||
mStartingTarget = ContainerType.HOTSEAT;
|
||||
} 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;
|
||||
mStartingTarget = ContainerType.WORKSPACE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +177,11 @@ 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);
|
||||
@@ -201,19 +198,11 @@ public class OverviewSwipeController extends AnimatorListenerAdapter
|
||||
}
|
||||
} 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);
|
||||
|
||||
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();
|
||||
mPendingAnimation = mRecentsView
|
||||
.createTaskDismissAnimation(mTaskBeingDragged, maxDuration);
|
||||
mCurrentAnimation = AnimatorPlaybackController
|
||||
.wrap(mPendingAnimation.anim, maxDuration);
|
||||
mEndDisplacement = -mTaskBeingDragged.getHeight();
|
||||
} else {
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
// TODO: Setup a zoom animation
|
||||
@@ -299,15 +288,17 @@ public class OverviewSwipeController extends AnimatorListenerAdapter
|
||||
}
|
||||
|
||||
private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.finish(wasSuccess);
|
||||
mPendingAnimation = null;
|
||||
}
|
||||
if (mTaskBeingDragged == null) {
|
||||
LauncherState state = wasSuccess ?
|
||||
(mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW;
|
||||
mLauncher.getStateManager().goToState(state, false);
|
||||
|
||||
} else if (wasSuccess) {
|
||||
if (mCurrentAnimationIsGoingUp) {
|
||||
mRecentsView.onTaskDismissed(mTaskBeingDragged);
|
||||
} else {
|
||||
if (!mCurrentAnimationIsGoingUp) {
|
||||
mTaskBeingDragged.launchTask(false);
|
||||
mLauncher.getUserEventDispatcher().logTaskLaunch(logAction,
|
||||
Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent());
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
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.Interpolators.ACCEL;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -28,84 +28,71 @@ 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.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
|
||||
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 boolean mIsRecentsSlidingInOrOut;
|
||||
|
||||
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();
|
||||
setVisibility(state.overviewUi);
|
||||
setTransitionProgress(state.overviewUi ? 1 : 0);
|
||||
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);
|
||||
LauncherState fromState = mLauncher.getStateManager().getState();
|
||||
mIsRecentsSlidingInOrOut = fromState == NORMAL && toState.overviewUi
|
||||
|| fromState.overviewUi && toState == NORMAL;
|
||||
|
||||
// Scroll to the workspace card before changing to the NORMAL state.
|
||||
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);
|
||||
mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0);
|
||||
progressAnim.setDuration(config.duration);
|
||||
progressAnim.setInterpolator(Interpolators.LINEAR);
|
||||
progressAnim.addListener(new AnimationSuccessListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mWorkspaceCard.setWorkspaceScrollingEnabled(toState == OVERVIEW);
|
||||
mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
|
||||
}
|
||||
});
|
||||
builder.play(progressAnim);
|
||||
|
||||
ObjectAnimator visibilityAnim = animateVisibility(toState == OVERVIEW);
|
||||
ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi);
|
||||
visibilityAnim.setDuration(config.duration);
|
||||
visibilityAnim.setInterpolator(Interpolators.LINEAR);
|
||||
builder.play(visibilityAnim);
|
||||
@@ -144,11 +131,14 @@ public class RecentsViewStateController implements StateHandler {
|
||||
|
||||
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));
|
||||
if (mIsRecentsSlidingInOrOut) {
|
||||
float interpolatedProgress = ACCEL.getInterpolation(mTransitionProgress.value);
|
||||
// Slide in from the side as we swipe.
|
||||
int translation = mRecentsView.getWidth();
|
||||
if (mRecentsView.isRtl()) {
|
||||
translation = -translation;
|
||||
}
|
||||
mRecentsView.setTranslationX(translation * (1 - interpolatedProgress));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,5 +148,9 @@ public class RecentsViewStateController implements StateHandler {
|
||||
|
||||
private void applyProgress() {
|
||||
mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
|
||||
if (mIsRecentsSlidingInOrOut) {
|
||||
// While animating into recents, update the visible task data as needed
|
||||
mRecentsView.loadVisibleTaskData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ 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;
|
||||
@@ -27,7 +26,6 @@ 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;
|
||||
|
||||
@@ -38,11 +36,9 @@ 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;
|
||||
@@ -52,8 +48,6 @@ 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
|
||||
*/
|
||||
@@ -112,8 +106,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
// 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);
|
||||
@@ -156,29 +148,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private void initSprings() {
|
||||
AllAppsContainerView appsView = mLauncher.getAppsView();
|
||||
|
||||
SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
|
||||
if (handler == null) {
|
||||
mSpringHandlers = new SpringAnimationHandler[0];
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<SpringAnimationHandler> 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) {
|
||||
@@ -214,10 +183,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
|
||||
mDetector.setDetectableScrollConditions(
|
||||
directionsToDetectScroll, ignoreSlopWhenSettling);
|
||||
|
||||
if (mSpringHandlers == null) {
|
||||
initSprings();
|
||||
}
|
||||
}
|
||||
|
||||
if (mNoIntercept) {
|
||||
@@ -230,9 +195,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
|
||||
@Override
|
||||
public boolean onControllerTouchEvent(MotionEvent ev) {
|
||||
for (SpringAnimationHandler h : mSpringHandlers) {
|
||||
h.addMovement(ev);
|
||||
}
|
||||
return mDetector.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@@ -283,10 +245,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
|
||||
updatePauseDetectorRangeFlag();
|
||||
}
|
||||
|
||||
for (SpringAnimationHandler h : mSpringHandlers) {
|
||||
h.skipToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private float getShiftRange() {
|
||||
@@ -329,13 +287,6 @@ public class TwoStepSwipeController extends AnimatorListenerAdapter
|
||||
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) {
|
||||
|
||||
@@ -18,37 +18,29 @@ package com.android.launcher3.uioverrides;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
import android.view.View;
|
||||
import android.view.View.AccessibilityDelegate;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
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.dragndrop.DragLayer;
|
||||
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) {
|
||||
return new TouchController[] {
|
||||
new IgnoreTouchesInQuickScrub(),
|
||||
new EdgeSwipeController(launcher),
|
||||
new TwoStepSwipeController(launcher),
|
||||
new OverviewSwipeController(launcher)};
|
||||
} else {
|
||||
return new TouchController[] {
|
||||
new IgnoreTouchesInQuickScrub(),
|
||||
new TwoStepSwipeController(launcher),
|
||||
new OverviewSwipeController(launcher)};
|
||||
}
|
||||
@@ -64,28 +56,40 @@ 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 shouldBackButtonBeVisible = launcher == null
|
||||
|| !launcher.isInState(NORMAL)
|
||||
|| !launcher.hasWindowFocus();
|
||||
if (!shouldBackButtonBeVisible) {
|
||||
// Show the back button if there is a floating view visible.
|
||||
DragLayer dragLayer = launcher.getDragLayer();
|
||||
for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = dragLayer.getChildAt(i);
|
||||
if (child instanceof AbstractFloatingView) {
|
||||
shouldBackButtonBeVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
OverviewInteractionState.setBackButtonVisible(launcher, shouldBackButtonBeVisible);
|
||||
}
|
||||
|
||||
public static void resetOverview(Launcher launcher) {
|
||||
RecentsView recents = launcher.getOverviewPanel();
|
||||
recents.reset();
|
||||
}
|
||||
|
||||
public static void onStart(Launcher launcher) {
|
||||
RecentsModel model = RecentsModel.getInstance(launcher);
|
||||
if (model != null) {
|
||||
model.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
public static void onTrimMemory(Launcher launcher, int level) {
|
||||
RecentsModel model = RecentsModel.getInstance(launcher);
|
||||
if (model != null) {
|
||||
model.onTrimMemory(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,12 @@ public class AnimatedFloat {
|
||||
}
|
||||
}
|
||||
|
||||
public void finishAnimation() {
|
||||
if (mValueAnimator != null && mValueAnimator.isRunning()) {
|
||||
mValueAnimator.end();
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectAnimator getCurrentAnimation() {
|
||||
return mValueAnimator;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.quickstep.TouchConsumer.InteractionType;
|
||||
public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
|
||||
|
||||
protected Runnable mGestureEndCallback;
|
||||
protected boolean mIsGoingToHome;
|
||||
|
||||
public void setGestureEndCallback(Runnable gestureEndCallback) {
|
||||
mGestureEndCallback = gestureEndCallback;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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<RecentsActivity> 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);
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
@@ -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<ApplicationInfo> getInstantApps() {
|
||||
try {
|
||||
List<ApplicationInfo> 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();
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,8 @@ public class MotionEventQueue {
|
||||
ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_RESET =
|
||||
ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_DEFER_INIT =
|
||||
ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
|
||||
|
||||
private final EventArray mEmptyArray = new EventArray();
|
||||
private final Object mExecutionLock = new Object();
|
||||
@@ -76,10 +78,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) {
|
||||
@@ -156,6 +158,9 @@ public class MotionEventQueue {
|
||||
case ACTION_RESET:
|
||||
mConsumer.reset();
|
||||
break;
|
||||
case ACTION_DEFER_INIT:
|
||||
mConsumer.deferInit();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid virtual event: " + event.getAction());
|
||||
}
|
||||
@@ -204,6 +209,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<MotionEvent> {
|
||||
|
||||
public int lastEventAction = ACTION_CANCEL;
|
||||
|
||||
@@ -17,7 +17,6 @@ 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;
|
||||
|
||||
@@ -51,6 +50,8 @@ import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.TouchConsumer.InteractionType;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -319,13 +320,14 @@ public class NavBarSwipeInteractionHandler extends BaseSwipeInteractionHandler i
|
||||
|
||||
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
|
||||
private void animateToProgress(float progress, long duration) {
|
||||
mIsGoingToHome = Float.compare(progress, 1) == 0;
|
||||
ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
|
||||
anim.setInterpolator(Interpolators.SCROLL);
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0)
|
||||
? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS);
|
||||
mStateCallback.setState(mIsGoingToHome
|
||||
? STATE_SCALED_SNAPSHOT_RECENTS : STATE_SCALED_SNAPSHOT_APP);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
|
||||
@@ -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<BitmapInfo> mDefaultIcons = new SparseArray<>();
|
||||
private final DrawableFactory mDrawableFactory;
|
||||
private LauncherIcons mLauncherIcons;
|
||||
|
||||
public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
|
||||
LruCache<ComponentName, ActivityInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.MotionEvent.INVALID_POINTER_ID;
|
||||
|
||||
import static com.android.quickstep.RemoteRunnable.executeSafely;
|
||||
import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON;
|
||||
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
|
||||
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_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;
|
||||
@@ -37,10 +39,9 @@ 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;
|
||||
@@ -63,50 +64,20 @@ 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 = DEBUG_SHOW_OVERVIEW_BUTTON
|
||||
? new int[] {HIT_TARGET_BACK, HIT_TARGET_OVERVIEW} : new int[] {HIT_TARGET_BACK};
|
||||
|
||||
private final RunningTaskInfo mRunningTask;
|
||||
private final RecentsModel mRecentsModel;
|
||||
@@ -115,6 +86,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
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;
|
||||
@@ -124,26 +96,24 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
private BaseSwipeInteractionHandler 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,
|
||||
MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
|
||||
@HitTarget int downHitTarget) {
|
||||
@HitTarget int downHitTarget, VelocityTracker velocityTracker) {
|
||||
super(base);
|
||||
mRunningTask = runningTaskInfo;
|
||||
mRecentsModel = recentsModel;
|
||||
mHomeIntent = homeIntent;
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mVelocityTracker = velocityTracker;
|
||||
mISystemUiProxy = systemUiProxy;
|
||||
mMainThreadExecutor = mainThreadExecutor;
|
||||
mBackgroundThreadChoreographer = backgroundThreadChoreographer;
|
||||
mDownHitTarget = downHitTarget;
|
||||
mIsDeferredDownTarget = Arrays.binarySearch(DEFERRED_HIT_TARGETS, downHitTarget) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,7 +132,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 (!isUsingScreenShot() && !mIsDeferredDownTarget) {
|
||||
startTouchTrackingForWindowAnimation(ev.getEventTime());
|
||||
}
|
||||
|
||||
@@ -204,7 +174,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
|
||||
if (isUsingScreenShot()) {
|
||||
startTouchTrackingForScreenshotAnimation();
|
||||
} else if (mDownHitTarget == HIT_TARGET_BACK) {
|
||||
} else if (mIsDeferredDownTarget) {
|
||||
// If we deferred starting the window animation on touch down, then
|
||||
// start tracking now
|
||||
startTouchTrackingForWindowAnimation(ev.getEventTime());
|
||||
@@ -312,7 +282,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
|
||||
// Create the shared handler
|
||||
final WindowTransformSwipeHandler handler =
|
||||
new WindowTransformSwipeHandler(mRunningTask, this);
|
||||
new WindowTransformSwipeHandler(mRunningTask, this, touchTimeMs);
|
||||
|
||||
// Preload the plan
|
||||
mRecentsModel.loadTasks(mRunningTask.id, null);
|
||||
@@ -350,16 +320,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() {
|
||||
@@ -417,6 +377,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
if (mInteractionHandler != null) {
|
||||
final BaseSwipeInteractionHandler handler = mInteractionHandler;
|
||||
mInteractionHandler = null;
|
||||
mIsGoingToHome = handler.mIsGoingToHome;
|
||||
mMainThreadExecutor.execute(handler::reset);
|
||||
}
|
||||
}
|
||||
@@ -471,4 +432,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager.RecentTaskInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.states.InternalStateHandler;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
|
||||
/**
|
||||
* Helper class to handle various atomic commands for switching between Overview.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public class OverviewCommandHelper extends InternalStateHandler {
|
||||
|
||||
private static final boolean DEBUG_START_FALLBACK_ACTIVITY = DEBUG_SHOW_OVERVIEW_BUTTON;
|
||||
|
||||
private final Context mContext;
|
||||
private final ActivityManagerWrapper mAM;
|
||||
|
||||
public final Intent homeIntent;
|
||||
public final ComponentName launcher;
|
||||
|
||||
private long mLastToggleTime;
|
||||
|
||||
public OverviewCommandHelper(Context context) {
|
||||
mContext = context;
|
||||
mAM = ActivityManagerWrapper.getInstance();
|
||||
|
||||
homeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(context.getPackageName())
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent, 0);
|
||||
launcher = new ComponentName(context.getPackageName(), info.activityInfo.name);
|
||||
// Clear the packageName as system can fail to dedupe it b/64108432
|
||||
homeIntent.setComponent(launcher).setPackage(null);
|
||||
}
|
||||
|
||||
public void onOverviewToggle() {
|
||||
if (DEBUG_START_FALLBACK_ACTIVITY) {
|
||||
mContext.startActivity(new Intent(mContext, RecentsActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
|
||||
return;
|
||||
}
|
||||
|
||||
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
|
||||
mLastToggleTime = SystemClock.elapsedRealtime();
|
||||
|
||||
if (isOverviewAlmostVisible()) {
|
||||
boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
|
||||
startNonLauncherTask(isQuickTap ? 2 : 1);
|
||||
} else {
|
||||
Intent intent = addToIntent(new Intent(homeIntent));
|
||||
mContext.startActivity(intent);
|
||||
initWhenReady();
|
||||
}
|
||||
}
|
||||
|
||||
private void startNonLauncherTask(int backStackCount) {
|
||||
for (RecentTaskInfo rti : mAM.getRecentTasks(backStackCount, UserHandle.myUserId())) {
|
||||
backStackCount--;
|
||||
if (backStackCount == 0) {
|
||||
mAM.startActivityFromRecents(rti.id, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON;
|
||||
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.os.Handler;
|
||||
@@ -60,7 +62,7 @@ public class OverviewInteractionState {
|
||||
}
|
||||
};
|
||||
|
||||
private static int sFlags;
|
||||
private static int sFlags = DEBUG_SHOW_OVERVIEW_BUTTON ? FLAG_SHOW_OVERVIEW_BUTTON : 0;
|
||||
|
||||
public static void setBackButtonVisible(Context context, boolean visible) {
|
||||
updateFlagOnUi(context, FLAG_HIDE_BACK_BUTTON, !visible);
|
||||
|
||||
@@ -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<Consumer<Boolean>> mEndListeners = new ArrayList<>();
|
||||
|
||||
public final AnimatorSet anim;
|
||||
|
||||
public PendingAnimation(AnimatorSet anim) {
|
||||
this.anim = anim;
|
||||
}
|
||||
|
||||
public void finish(boolean isSuccess) {
|
||||
for (Consumer<Boolean> listeners : mEndListeners) {
|
||||
listeners.accept(isSuccess);
|
||||
}
|
||||
mEndListeners.clear();
|
||||
}
|
||||
|
||||
public void addEndListener(Consumer<Boolean> listener) {
|
||||
mEndListeners.add(listener);
|
||||
}
|
||||
}
|
||||
@@ -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_SWITCH_START_DURATION = 210;
|
||||
|
||||
private static final boolean ENABLE_AUTO_ADVANCE = true;
|
||||
private static final int NUM_QUICK_SCRUB_SECTIONS = 3;
|
||||
@@ -47,15 +47,17 @@ 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 mQuickSwitched;
|
||||
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 +67,14 @@ 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();
|
||||
mQuickSwitched = false;
|
||||
mFinishedTransitionToQuickScrub = false;
|
||||
|
||||
snapToNextTaskIfAvailable();
|
||||
mActivity.getUserEventDispatcher().resetActionDurationMillis();
|
||||
}
|
||||
|
||||
public void onQuickScrubEnd() {
|
||||
@@ -78,11 +84,7 @@ public class QuickScrubController implements OnAlarmListener {
|
||||
}
|
||||
int page = mRecentsView.getNextPage();
|
||||
Runnable launchTaskRunnable = () -> {
|
||||
if (page < mRecentsView.getFirstTaskIndex()) {
|
||||
mRecentsView.getPageAt(page).performClick();
|
||||
} else {
|
||||
((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
|
||||
}
|
||||
((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
|
||||
};
|
||||
int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
|
||||
* QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
|
||||
@@ -93,8 +95,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 +104,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
|
||||
@@ -116,36 +120,45 @@ 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);
|
||||
mQuickSwitched = true;
|
||||
quickSwitchIfReady();
|
||||
}
|
||||
|
||||
public void snapToPageForCurrentQuickScrubSection() {
|
||||
if (mInQuickScrub) {
|
||||
goToPageWithHaptic(mRecentsView.getFirstTaskIndex() + mQuickScrubSection);
|
||||
public void onFinishedTransitionToQuickScrub() {
|
||||
mFinishedTransitionToQuickScrub = true;
|
||||
quickSwitchIfReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately launches the current task (which we snapped to in onQuickScrubStart) if we've
|
||||
* gotten the onQuickSwitch callback and the transition to quick scrub has completed.
|
||||
*/
|
||||
private void quickSwitchIfReady() {
|
||||
if (mQuickSwitched && mFinishedTransitionToQuickScrub) {
|
||||
onQuickScrubEnd();
|
||||
mActivity.getUserEventDispatcher().logActionOnControl(Touch.FLING,
|
||||
ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
|
||||
ContainerType.WORKSPACE : ContainerType.APP);
|
||||
}
|
||||
}
|
||||
|
||||
public void snapToNextTaskIfAvailable() {
|
||||
if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
|
||||
int toPage = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
|
||||
goToPageWithHaptic(toPage, QUICK_SWITCH_START_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
private void goToPageWithHaptic(int pageToGoTo) {
|
||||
pageToGoTo = Utilities.boundToRange(pageToGoTo, mStartPage, mRecentsView.getPageCount() - 1);
|
||||
goToPageWithHaptic(pageToGoTo, -1);
|
||||
}
|
||||
|
||||
private void goToPageWithHaptic(int pageToGoTo, int overrideDuration) {
|
||||
pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
|
||||
if (pageToGoTo != mRecentsView.getNextPage()) {
|
||||
int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage())
|
||||
* QUICKSCRUB_SNAP_DURATION_PER_PAGE;
|
||||
int duration = overrideDuration > -1 ? overrideDuration
|
||||
: Math.abs(pageToGoTo - mRecentsView.getNextPage())
|
||||
* QUICKSCRUB_SNAP_DURATION_PER_PAGE;
|
||||
mRecentsView.snapToPage(pageToGoTo, duration);
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
@@ -158,7 +171,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;
|
||||
|
||||
@@ -15,34 +15,29 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
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.BaseActivity;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* A simple activity to show the recently launched tasks
|
||||
*/
|
||||
public class RecentsActivity extends ListActivity {
|
||||
|
||||
private ArrayAdapter<Task> mAdapter;
|
||||
public class RecentsActivity extends BaseActivity {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -51,7 +53,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 +84,16 @@ public class RecentsModel extends TaskStackChangeListener {
|
||||
private int mTaskChangeId;
|
||||
private ISystemUiProxy mSystemUiProxy;
|
||||
private boolean mClearAssistCacheOnStackChange = true;
|
||||
private final boolean mPreloadTasksInBackground;
|
||||
|
||||
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,8 +107,6 @@ public class RecentsModel extends TaskStackChangeListener {
|
||||
}
|
||||
};
|
||||
mRecentsTaskLoader.startLoader(mContext);
|
||||
|
||||
mMainThreadExecutor = new MainThreadExecutor();
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
|
||||
|
||||
mTaskChangeId = 1;
|
||||
@@ -162,6 +167,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 = false;
|
||||
plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId);
|
||||
mRecentsTaskLoader.loadTasks(plan, launchOpts);
|
||||
}
|
||||
|
||||
public boolean isLoadPlanValid(int resultId) {
|
||||
return mTaskChangeId == resultId;
|
||||
}
|
||||
@@ -178,6 +208,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(() -> {
|
||||
|
||||
@@ -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.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.InsettableFrameLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
public class RecentsRootView extends InsettableFrameLayout {
|
||||
|
||||
private final BaseActivity mActivity;
|
||||
|
||||
public RecentsRootView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mActivity = BaseActivity.fromContext(context);
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -16,27 +16,39 @@
|
||||
|
||||
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.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.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;
|
||||
|
||||
/**
|
||||
@@ -86,9 +98,10 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
|
||||
}
|
||||
}
|
||||
|
||||
public static class SplitScreen extends TaskSystemShortcut {
|
||||
public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener {
|
||||
|
||||
private Handler mHandler;
|
||||
private TaskView mTaskView;
|
||||
|
||||
public SplitScreen() {
|
||||
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
|
||||
@@ -104,22 +117,55 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
|
||||
if (!task.isDockable) {
|
||||
return null;
|
||||
}
|
||||
mTaskView = taskView;
|
||||
return (v -> {
|
||||
final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(true);
|
||||
final Consumer<Boolean> resultCallback = success -> {
|
||||
if (success) {
|
||||
launcher.<RecentsView>getOverviewPanel().removeView(taskView);
|
||||
}
|
||||
};
|
||||
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
|
||||
task.key, options, resultCallback, mHandler);
|
||||
AbstractFloatingView.closeOpenViews(launcher, true,
|
||||
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
|
||||
|
||||
// TODO improve transition; see:
|
||||
// DockedFirstAnimationFrameEvent
|
||||
// RecentsTransitionHelper#composeDockAnimationSpec
|
||||
// WindowManagerWrapper#overridePendingAppTransitionMultiThumbFuture
|
||||
if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id,
|
||||
ActivityOptionsCompat.makeSplitScreenOptions(true))) {
|
||||
ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy();
|
||||
try {
|
||||
sysUiProxy.onSplitScreenInvoked();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
|
||||
return;
|
||||
}
|
||||
|
||||
final Runnable animStartedListener = () -> {
|
||||
mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
|
||||
launcher.<RecentsView>getOverviewPanel().removeView(taskView);
|
||||
};
|
||||
|
||||
final int[] position = new int[2];
|
||||
taskView.getLocationOnScreen(position);
|
||||
final int width = (int) (taskView.getWidth() * taskView.getScaleX());
|
||||
final int height = (int) (taskView.getHeight() * taskView.getScaleY());
|
||||
final Rect taskBounds = new Rect(position[0], position[1],
|
||||
position[0] + width, position[1] + height);
|
||||
|
||||
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
|
||||
taskBounds.width(), taskBounds.height(), taskView, 1f, Color.BLACK);
|
||||
AppTransitionAnimationSpecsFuture future =
|
||||
new AppTransitionAnimationSpecsFuture(mHandler) {
|
||||
@Override
|
||||
public List<AppTransitionAnimationSpecCompat> composeSpecs() {
|
||||
return Collections.singletonList(new AppTransitionAnimationSpecCompat(
|
||||
task.key.id, thumbnail, taskBounds));
|
||||
}
|
||||
};
|
||||
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
|
||||
future, animStartedListener, mHandler, true /* scaleUp */);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
mTaskView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
WindowManagerWrapper.getInstance().endProlongedAnimations();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pin extends TaskSystemShortcut {
|
||||
@@ -137,7 +183,11 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
|
||||
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 -> {
|
||||
|
||||
@@ -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;
|
||||
@@ -64,4 +62,14 @@ public interface TouchConsumer extends Consumer<MotionEvent> {
|
||||
default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default void deferInit() { }
|
||||
|
||||
default boolean deferNextEventToMainThread() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean forceToLauncherConsumer() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,13 @@ 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 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 +38,18 @@ 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_SHOW_OVERVIEW_BUTTON = false;
|
||||
|
||||
private static final SparseArray<String> sMotionEventNames;
|
||||
|
||||
static {
|
||||
@@ -117,7 +120,6 @@ public class TouchInteractionService extends Service {
|
||||
@Override
|
||||
public void onQuickScrubStart() {
|
||||
mEventQueue.onQuickScrubStart();
|
||||
sQuickScrubEnabled = true;
|
||||
TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart");
|
||||
}
|
||||
|
||||
@@ -130,35 +132,37 @@ 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) { }
|
||||
|
||||
@Override
|
||||
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
|
||||
};
|
||||
|
||||
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 Choreographer mMainThreadChoreographer;
|
||||
private Choreographer mBackgroundThreadChoreographer;
|
||||
private MotionEventQueue mNoOpEventQueue;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -166,19 +170,9 @@ 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);
|
||||
|
||||
sConnected = true;
|
||||
|
||||
@@ -190,7 +184,6 @@ public class TouchInteractionService extends Service {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
sConnected = false;
|
||||
sQuickScrubEnabled = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -201,31 +194,45 @@ public class TouchInteractionService extends Service {
|
||||
}
|
||||
|
||||
private void onBinderPreMotionEvent(@HitTarget int downHitTarget) {
|
||||
mRunningTask = mAM.getRunningTask();
|
||||
|
||||
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, mISystemUiProxy, 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 {
|
||||
@@ -310,13 +317,12 @@ public class TouchInteractionService extends Service {
|
||||
if (TouchConsumer.isInteractionQuick(interactionType)) {
|
||||
Runnable action = () -> {
|
||||
Runnable onComplete = null;
|
||||
if (interactionType == INTERACTION_QUICK_SCRUB) {
|
||||
mQuickScrubController.onQuickScrubStart(true);
|
||||
} else if (interactionType == INTERACTION_QUICK_SWITCH) {
|
||||
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, onComplete);
|
||||
mQuickScrubController.onQuickScrubStart(fromState == NORMAL);
|
||||
};
|
||||
|
||||
if (mLauncher.getWorkspace().runOnOverlayHidden(action)) {
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
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 static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
|
||||
import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION;
|
||||
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
|
||||
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
|
||||
@@ -32,16 +35,17 @@ 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;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.util.Log;
|
||||
@@ -67,9 +71,12 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||
import com.android.quickstep.TouchConsumer.InteractionType;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.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;
|
||||
@@ -77,6 +84,40 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
class EventLogTags {
|
||||
private EventLogTags() {
|
||||
} // don't instantiate
|
||||
|
||||
/** 524292 sysui_multi_action (content|4) */
|
||||
public static final int SYSUI_MULTI_ACTION = 524292;
|
||||
|
||||
public static void writeSysuiMultiAction(Object[] content) {
|
||||
android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content);
|
||||
}
|
||||
}
|
||||
|
||||
class MetricsLogger {
|
||||
private static MetricsLogger sMetricsLogger;
|
||||
|
||||
private static MetricsLogger getLogger() {
|
||||
if (sMetricsLogger == null) {
|
||||
sMetricsLogger = new MetricsLogger();
|
||||
}
|
||||
return sMetricsLogger;
|
||||
}
|
||||
|
||||
protected void saveLog(Object[] rep) {
|
||||
EventLogTags.writeSysuiMultiAction(rep);
|
||||
}
|
||||
|
||||
public void write(LogMaker content) {
|
||||
if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) {
|
||||
content.setType(4/*MetricsEvent.TYPE_ACTION*/);
|
||||
}
|
||||
saveLog(content.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
|
||||
@@ -189,11 +230,17 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
|
||||
private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
|
||||
private Matrix mTmpMatrix = new Matrix();
|
||||
private final long mTouchTimeMs;
|
||||
private long mLauncherFrameDrawnTime;
|
||||
private final MetricsLogger mMetricsLogger = new MetricsLogger();
|
||||
|
||||
WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) {
|
||||
WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs) {
|
||||
mContext = context;
|
||||
mRunningTaskId = runningTaskInfo.id;
|
||||
mInputConsumer.registerInputConsumer();
|
||||
mTouchTimeMs = touchTimeMs;
|
||||
// Register the input consumer on the UI thread, to ensure that it runs after any pending
|
||||
// unregister calls
|
||||
mMainExecutor.execute(mInputConsumer::registerInputConsumer);
|
||||
initStateCallbacks();
|
||||
}
|
||||
|
||||
@@ -323,7 +370,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
|
||||
// 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);
|
||||
mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
|
||||
|
||||
mRecentsView = mLauncher.getOverviewPanel();
|
||||
mQuickScrubController = mRecentsView.getQuickScrubController();
|
||||
mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
|
||||
@@ -416,11 +464,26 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
if (mLauncherDrawnCallback != null) {
|
||||
mLauncherDrawnCallback.run();
|
||||
}
|
||||
mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
private void initializeLauncherAnimationController() {
|
||||
mLauncherLayoutListener.setHandler(this);
|
||||
onLauncherLayoutChanged();
|
||||
|
||||
final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs;
|
||||
// Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for
|
||||
// "Recents" activity for app transition tests for the app-to-recents case.
|
||||
final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/);
|
||||
builder.setPackageName("com.android.systemui");
|
||||
builder.addTaggedData(871/*FIELD_CLASS_NAME*/,
|
||||
"com.android.systemui.recents.RecentsActivity");
|
||||
builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/,
|
||||
transitionDelay);
|
||||
mMetricsLogger.write(builder);
|
||||
if (LatencyTrackerCompat.isEnabled(mContext)) {
|
||||
LatencyTrackerCompat.logToggleRecents((int) transitionDelay);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateInteractionType(@InteractionType int interactionType) {
|
||||
@@ -442,6 +505,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
}
|
||||
|
||||
private void onQuickInteractionStart() {
|
||||
mLauncher.getStateManager().goToState(FAST_OVERVIEW,
|
||||
mWasLauncherAlreadyVisible || mGestureStarted);
|
||||
mQuickScrubController.onQuickScrubStart(false);
|
||||
}
|
||||
|
||||
@@ -509,8 +574,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,9 +595,8 @@ 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) {
|
||||
@@ -658,13 +727,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,7 +755,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
}
|
||||
|
||||
private void invalidateHandler() {
|
||||
mCurrentShift.cancelAnimation();
|
||||
mCurrentShift.finishAnimation();
|
||||
|
||||
if (mGestureEndCallback != null) {
|
||||
mGestureEndCallback.run();
|
||||
@@ -701,7 +771,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
|
||||
mLauncherLayoutListener.close(false);
|
||||
|
||||
// Restore the requested orientation to the user preference after the gesture has ended
|
||||
mLauncher.updateRequestedOrientation();
|
||||
mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
|
||||
|
||||
mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.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.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
|
||||
*/
|
||||
public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable {
|
||||
|
||||
private Bitmap mScrim;
|
||||
private Paint mFadePaint;
|
||||
private Shader mFadeShader;
|
||||
private Matrix mFadeMatrix;
|
||||
private boolean mScrimOnLeft;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
if (dp.isVerticalBarLayout()) {
|
||||
boolean wasScrimOnLeft = mScrimOnLeft;
|
||||
mScrimOnLeft = dp.isSeascape();
|
||||
|
||||
if (mScrim == null || wasScrimOnLeft != mScrimOnLeft) {
|
||||
Drawable scrim = getContext().getDrawable(mScrimOnLeft
|
||||
? R.drawable.recents_horizontal_scrim_left
|
||||
: R.drawable.recents_horizontal_scrim_right);
|
||||
if (scrim instanceof BitmapDrawable) {
|
||||
mScrim = ((BitmapDrawable) scrim).getBitmap();
|
||||
mFadePaint = new Paint();
|
||||
mFadePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
|
||||
mFadeShader = new BitmapShader(mScrim, TileMode.CLAMP, TileMode.REPEAT);
|
||||
mFadeMatrix = new Matrix();
|
||||
} else {
|
||||
mScrim = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mScrim = null;
|
||||
mFadePaint = null;
|
||||
mFadeShader = null;
|
||||
mFadeMatrix = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mScrim == null) {
|
||||
super.draw(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
|
||||
|
||||
int length = mScrim.getWidth();
|
||||
int height = getHeight();
|
||||
int saveCount = canvas.getSaveCount();
|
||||
|
||||
int scrimLeft;
|
||||
if (mScrimOnLeft) {
|
||||
scrimLeft = getScrollX();
|
||||
} else {
|
||||
scrimLeft = getScrollX() + getWidth() - length;
|
||||
}
|
||||
canvas.saveLayer(scrimLeft, 0, scrimLeft + length, height, null, flags);
|
||||
super.draw(canvas);
|
||||
|
||||
mFadeMatrix.setTranslate(scrimLeft, 0);
|
||||
mFadeShader.setLocalMatrix(mFadeMatrix);
|
||||
mFadePaint.setShader(mFadeShader);
|
||||
canvas.drawRect(scrimLeft, 0, scrimLeft + length, height, mFadePaint);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAllTasksRemoved() {
|
||||
mActivity.getStateManager().goToState(NORMAL);
|
||||
}
|
||||
}
|
||||
+257
-233
@@ -14,40 +14,36 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.quickstep;
|
||||
package com.android.quickstep.views;
|
||||
|
||||
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 static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.animation.LayoutTransition;
|
||||
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.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.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
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.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;
|
||||
@@ -62,29 +58,30 @@ import java.util.ArrayList;
|
||||
/**
|
||||
* A list of recent tasks.
|
||||
*/
|
||||
public class RecentsView extends PagedView implements Insettable {
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public abstract class RecentsView<T extends BaseActivity>
|
||||
extends PagedView implements OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final String PREF_FLIP_RECENTS = "pref_flip_recents";
|
||||
|
||||
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;
|
||||
protected final T mActivity;
|
||||
private final QuickScrubController mQuickScrubController;
|
||||
private final float mFastFlingVelocity;
|
||||
private final RecentsModel mModel;
|
||||
|
||||
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;
|
||||
// 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 TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
|
||||
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
|
||||
@Override
|
||||
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
|
||||
for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
|
||||
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);
|
||||
@@ -94,47 +91,51 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
private boolean mOverviewStateEnabled;
|
||||
private boolean mTaskStackListenerRegistered;
|
||||
private Runnable mNextPageSwitchRunnable;
|
||||
|
||||
public RecentsView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
private PendingAnimation mPendingAnimation;
|
||||
|
||||
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);
|
||||
mFastFlingVelocity = getResources()
|
||||
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
|
||||
mActivity = (T) BaseActivity.fromContext(context);
|
||||
mQuickScrubController = new QuickScrubController(mActivity, this);
|
||||
mModel = RecentsModel.getInstance(context);
|
||||
|
||||
mScrollState.isRtl = mIsRtl;
|
||||
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 = mFirstTaskIndex; i < getChildCount(); i++) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
final TaskView taskView = (TaskView) getChildAt(i);
|
||||
if (taskView.getTask().key.id == taskId) {
|
||||
taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
|
||||
@@ -145,23 +146,6 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
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);
|
||||
@@ -172,62 +156,37 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
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 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);
|
||||
public void onViewRemoved(View child) {
|
||||
super.onViewRemoved(child);
|
||||
|
||||
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;
|
||||
// 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 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++) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
if (tv.getTask().key.id == taskId) {
|
||||
return tv;
|
||||
@@ -236,14 +195,6 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStateController(RecentsViewStateController stateController) {
|
||||
mStateController = stateController;
|
||||
}
|
||||
|
||||
public RecentsViewStateController getStateController() {
|
||||
return mStateController;
|
||||
}
|
||||
|
||||
public void setOverviewStateEnabled(boolean enabled) {
|
||||
mOverviewStateEnabled = enabled;
|
||||
updateTaskStackListenerState();
|
||||
@@ -263,7 +214,10 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
}
|
||||
|
||||
private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
|
||||
final RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.addEndListener((b) -> applyLoadPlan(loadPlan));
|
||||
return;
|
||||
}
|
||||
TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
|
||||
if (stack == null) {
|
||||
removeAllViews();
|
||||
@@ -276,9 +230,8 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
// necessary)
|
||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
|
||||
setLayoutTransition(null);
|
||||
|
||||
final int requiredChildCount = tasks.size() + mFirstTaskIndex;
|
||||
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);
|
||||
@@ -286,26 +239,36 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
while (getChildCount() > requiredChildCount) {
|
||||
final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
|
||||
removeView(taskView);
|
||||
loader.unloadTaskData(taskView.getTask());
|
||||
}
|
||||
setLayoutTransition(mLayoutTransition);
|
||||
|
||||
// Unload existing visible task data
|
||||
unloadVisibleTaskData();
|
||||
|
||||
// Rebind and reset all task views
|
||||
for (int i = tasks.size() - 1; i >= 0; i--) {
|
||||
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(tasks.size() - i - 1 + mFirstTaskIndex);
|
||||
final TaskView taskView = (TaskView) getChildAt(pageIndex);
|
||||
taskView.bind(task);
|
||||
taskView.resetVisualProperties();
|
||||
loader.loadTaskData(task);
|
||||
}
|
||||
updateCurveProperties();
|
||||
resetTaskVisuals();
|
||||
applyIconScale(false /* animate */);
|
||||
|
||||
if (oldChildCount != getChildCount()) {
|
||||
mQuickScrubController.snapToPageForCurrentQuickScrubSection();
|
||||
mQuickScrubController.snapToNextTaskIfAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetTaskVisuals() {
|
||||
for (int i = getChildCount() - 1; i >= 0; i--) {
|
||||
((TaskView) getChildAt(i)).resetVisualProperties();
|
||||
}
|
||||
|
||||
updateCurveProperties();
|
||||
// Update the set of visible task's data
|
||||
loadVisibleTaskData();
|
||||
}
|
||||
|
||||
private void updateTaskStackListenerState() {
|
||||
boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow()
|
||||
&& getWindowVisibility() == VISIBLE;
|
||||
@@ -322,7 +285,7 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
}
|
||||
}
|
||||
|
||||
private static Rect getPadding(DeviceProfile profile, Context context) {
|
||||
protected static Rect getPadding(DeviceProfile profile, Context context) {
|
||||
WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets);
|
||||
Rect padding = new Rect(profile.workspacePadding);
|
||||
|
||||
@@ -331,11 +294,14 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
|
||||
float overviewHeight, overviewWidth;
|
||||
if (profile.isVerticalBarLayout()) {
|
||||
float scrimLength = context.getResources()
|
||||
.getDimension(R.dimen.recents_page_fade_length);
|
||||
float maxPadding = Math.max(padding.left, padding.right);
|
||||
|
||||
// Use the same padding on both sides for symmetry.
|
||||
float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
|
||||
float availableHeight = profile.availableHeightPx - padding.top
|
||||
- sTempStableInsets.top - Math.max(padding.bottom,
|
||||
profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context)));
|
||||
float availableWidth = taskWidth - 2 * Math.max(maxPadding, scrimLength);
|
||||
float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
|
||||
- sTempStableInsets.top;
|
||||
float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
|
||||
overviewHeight = taskHeight * scaledRatio;
|
||||
overviewWidth = taskWidth * scaledRatio;
|
||||
@@ -352,33 +318,6 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
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();
|
||||
@@ -392,9 +331,24 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
super.computeScroll();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,8 +358,7 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
|
||||
return;
|
||||
}
|
||||
final int halfPageWidth = mScrollState.halfPageWidth = getNormalChildWidth() / 2;
|
||||
mScrollState.lastScrollType = SCROLL_TYPE_NONE;
|
||||
final int halfPageWidth = getNormalChildWidth() / 2;
|
||||
final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth;
|
||||
final int halfScreenWidth = getMeasuredWidth() / 2;
|
||||
final int pageSpacing = mPageSpacing;
|
||||
@@ -413,36 +366,72 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
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 pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth;
|
||||
float distanceFromScreenCenter = screenCenter - pageCenter;
|
||||
float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
|
||||
mScrollState.linearInterpolation = Math.min(1,
|
||||
Math.abs(mScrollState.distanceFromScreenCenter) / distanceToReachEdge);
|
||||
mScrollState.lastScrollType = ((PageCallbacks) page).onPageScroll(mScrollState);
|
||||
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
|
||||
((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);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public int getTaskCount() {
|
||||
return getChildCount() - mFirstTaskIndex;
|
||||
}
|
||||
|
||||
public int getRunningTaskId() {
|
||||
return mRunningTaskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the view if anything in recents changed.
|
||||
*/
|
||||
@@ -462,26 +451,24 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
*/
|
||||
public void showTask(int runningTaskId) {
|
||||
boolean needsReload = false;
|
||||
if (getTaskCount() == 0) {
|
||||
if (getChildCount() == 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);
|
||||
addView(taskView, 0);
|
||||
}
|
||||
mRunningTaskId = runningTaskId;
|
||||
setCurrentPage(0);
|
||||
if (!needsReload) {
|
||||
needsReload = !mModel.isLoadPlanValid(mLoadPlanId);
|
||||
}
|
||||
if (needsReload) {
|
||||
mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
|
||||
} else {
|
||||
loadVisibleTaskData();
|
||||
}
|
||||
mRunningTaskId = runningTaskId;
|
||||
setCurrentPage(mFirstTaskIndex);
|
||||
if (mCurrentPage >= mFirstTaskIndex) {
|
||||
getPageAt(mCurrentPage).setAlpha(0);
|
||||
}
|
||||
getPageAt(mCurrentPage).setAlpha(0);
|
||||
}
|
||||
|
||||
public QuickScrubController getQuickScrubController() {
|
||||
@@ -498,7 +485,7 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
|
||||
private void applyIconScale(boolean animate) {
|
||||
float scale = mFirstTaskIconScaledDown ? 0 : 1;
|
||||
TaskView firstTask = (TaskView) getChildAt(mFirstTaskIndex);
|
||||
TaskView firstTask = (TaskView) getChildAt(0);
|
||||
if (firstTask != null) {
|
||||
if (animate) {
|
||||
firstTask.animateIconToScale(scale);
|
||||
@@ -508,57 +495,94 @@ public class RecentsView extends PagedView implements Insettable {
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
* Updates the page UI based on scroll params.
|
||||
*/
|
||||
int onPageScroll(ScrollState scrollState);
|
||||
default void onPageScroll(ScrollState scrollState) {};
|
||||
}
|
||||
|
||||
public static class ScrollState {
|
||||
|
||||
public boolean isRtl;
|
||||
public int lastScrollType;
|
||||
|
||||
public int halfPageWidth;
|
||||
public float distanceFromScreenCenter;
|
||||
/**
|
||||
* 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 float prevPageExtraWidth;
|
||||
public PendingAnimation createTaskDismissAnimation(TaskView taskView, 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) {
|
||||
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
|
||||
taskView.setTranslationZ(0.1f);
|
||||
|
||||
mPendingAnimation = pendingAnimation;
|
||||
mPendingAnimation.addEndListener((isSuccess) -> {
|
||||
if (isSuccess) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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;
|
||||
@@ -40,6 +40,8 @@ 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.quickstep.TaskSystemShortcut;
|
||||
import com.android.quickstep.TaskUtils;
|
||||
|
||||
/**
|
||||
* Contains options for a recent task when long-pressing its icon.
|
||||
+6
-3
@@ -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
|
||||
+9
-36
@@ -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;
|
||||
@@ -33,8 +30,8 @@ import android.widget.ImageView;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
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,15 @@ 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;
|
||||
|
||||
private static final long SCALE_ICON_DURATION = 120;
|
||||
|
||||
@@ -159,38 +154,16 @@ 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static final class TaskOutlineProvider extends ViewOutlineProvider {
|
||||
@@ -15,5 +15,5 @@
|
||||
-->
|
||||
<com.android.launcher3.graphics.ShadowDrawable
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_info_no_shadow"
|
||||
android:src="@drawable/ic_setting"
|
||||
android:elevation="@dimen/drop_target_shadow_elevation" />
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -13,8 +13,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorAccent" />
|
||||
<size android:height="1dp" />
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="?attr/popupColorPrimary" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
@@ -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" >
|
||||
<path
|
||||
android:fillColor="?android:attr/textColorPrimary"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M42.8,28.4l-3.88-2.9c0.06-0.5,0.08-1,0.08-1.52s-0.02-1.02-0.08-1.52l3.88-2.86c0.84-0.62,1.04-1.88,
|
||||
0.48-2.82l-3.2-5.52c-0.56-0.96-1.76-1.4-2.72-1l-4.28,1.82c-0.96-0.74-2.02-1.36-3.14-1.84l-0.54-4.4C29.28,4.8,28.28,4,
|
||||
27.18,4h-6.36c-1.1,0-2.1,0.8-2.22,1.84l-0.52,4.38c-1.14,0.48-2.2,1.1-3.16,1.84l-4.28-1.82c-0.96-0.4-2.16,0.04-2.72,
|
||||
|
||||
@@ -29,20 +29,13 @@
|
||||
|
||||
<include layout="@layout/all_apps_rv_layout" />
|
||||
|
||||
<include layout="@layout/all_apps_fast_scroller" />
|
||||
|
||||
<include layout="@layout/all_apps_floating_header" />
|
||||
|
||||
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
|
||||
platform bug, which prevents using custom attributes in <include> tag -->
|
||||
<include
|
||||
android:id="@id/search_container_all_apps"
|
||||
layout="?android:attr/keyboardLayout"/>
|
||||
layout="@layout/search_container_all_apps"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/nav_bar_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="?attr/allAppsNavBarScrimColor" />
|
||||
<include layout="@layout/all_apps_fast_scroller" />
|
||||
</com.android.launcher3.allapps.AllAppsContainerView>
|
||||
@@ -22,5 +22,4 @@
|
||||
android:layout_below="@id/search_container_all_apps"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
android:overScrollMode="never" />
|
||||
android:focusable="true" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -32,18 +32,8 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/remove_drop_target_label" />
|
||||
|
||||
<!-- App Info -->
|
||||
<com.android.launcher3.InfoDropTarget
|
||||
android:id="@+id/info_target_text"
|
||||
style="@style/DropTargetButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:text="@string/app_info_drop_target_label" />
|
||||
|
||||
<!-- Uninstall target -->
|
||||
<com.android.launcher3.UninstallDropTarget
|
||||
<com.android.launcher3.SecondaryDropTarget
|
||||
android:id="@+id/uninstall_target_text"
|
||||
style="@style/DropTargetButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -44,11 +44,6 @@
|
||||
layout="@layout/overview_panel"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.android.launcher3.views.AllAppsScrim
|
||||
android:id="@+id/all_apps_scrim"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<!-- DO NOT CHANGE THE ID -->
|
||||
<include
|
||||
android:id="@+id/hotseat"
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.uioverrides.OptionsPopupView
|
||||
<com.android.launcher3.views.OptionsPopupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
@@ -94,4 +94,4 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.launcher3.uioverrides.OptionsPopupView>
|
||||
</com.android.launcher3.views.OptionsPopupView>
|
||||
@@ -14,61 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.uioverrides.OverviewPanel
|
||||
<Space
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:theme="@style/HomeScreenElementTheme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:gravity="top"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wallpaper_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTop="@drawable/ic_wallpaper"
|
||||
android:drawableTint="?attr/workspaceTextColor"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center_horizontal"
|
||||
android:stateListAnimator="@animator/overview_button_anim"
|
||||
android:text="@string/wallpaper_button_text"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?attr/workspaceTextColor"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTop="@drawable/ic_widget"
|
||||
android:drawableTint="?attr/workspaceTextColor"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center_horizontal"
|
||||
android:stateListAnimator="@animator/overview_button_anim"
|
||||
android:text="@string/widget_button_text"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?attr/workspaceTextColor"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTop="@drawable/ic_setting"
|
||||
android:drawableTint="?attr/workspaceTextColor"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center_horizontal"
|
||||
android:stateListAnimator="@animator/overview_button_anim"
|
||||
android:text="@string/settings_button_text"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?attr/workspaceTextColor"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</com.android.launcher3.uioverrides.OverviewPanel>
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp" />
|
||||
@@ -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" >
|
||||
|
||||
<!--
|
||||
Note: The following relation should always be true so that the shadows are aligned properly
|
||||
search_box_input.layout_marginBottom
|
||||
== search_divider.layout_marginBottom
|
||||
== - (search_container.layout_marginBottom)
|
||||
>= 5dp
|
||||
-->
|
||||
<com.android.launcher3.ExtendedEditText
|
||||
android:id="@+id/search_box_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_search_bar_field_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/transparent"
|
||||
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:saveEnabled="false"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textColorHint="@drawable/all_apps_search_hint"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
|
||||
We translate the header down, and its content up by the same amount, so that it gets
|
||||
clipped by the parent, making it look like the divider was scrolled out of the view. -->
|
||||
<FrameLayout
|
||||
android:id="@+id/search_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="8dp" >
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/all_apps_search_divider"/>
|
||||
</FrameLayout>
|
||||
</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
|
||||
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" />
|
||||
@@ -34,7 +34,6 @@
|
||||
android:fontFamily="sans-serif"
|
||||
launcher:iconDisplay="shortcut_popup"
|
||||
launcher:layoutHorizontal="true"
|
||||
launcher:deferShadowGeneration="true"
|
||||
android:focusable="false" />
|
||||
|
||||
<View
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
android:orientation="vertical"
|
||||
android:theme="?attr/widgetsTheme" >
|
||||
|
||||
<com.android.launcher3.graphics.GradientView
|
||||
android:id="@+id/gradient_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.android.launcher3.views.TopRoundedCornerView
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
@@ -36,29 +31,22 @@
|
||||
android:id="@+id/widgets_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
/>
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
|
||||
|
||||
<com.android.launcher3.views.RecyclerViewFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="@dimen/fastscroll_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
|
||||
|
||||
<View
|
||||
android:id="@+id/nav_bar_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/allAppsNavBarScrimColor"
|
||||
android:focusable="false" />
|
||||
</com.android.launcher3.views.TopRoundedCornerView>
|
||||
</com.android.launcher3.widget.WidgetsFullSheet>
|
||||
@@ -42,7 +42,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" />
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_bottom_padding"
|
||||
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
|
||||
android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Kry werkprogramme hier"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Elke werkprogram het \'n oranje kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou Tuisskerm vir makliker toegang."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Bestuur deur jou organisasie"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Kennisgewings en programme is af"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"ሥራ"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"የሥራ መገለጫ"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"የስራ መተግበሪያዎችን እዚህ ያግኙ"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"እያንዳንዱ የሥራ መተግበሪያ ብርቱካናማ ባጅ አለው እና በእርስዎ ድርጅት በኩል ደህንነቱ ተጠብቋል። ለቀለለ መዳረሻ መተግበሪያዎችን ወደ የእርስዎ መነሻ ማያ ገጽ ያንቀሳቅሱ።"</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"በእርስዎ ድርጅት የሚተዳደር"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል"</string>
|
||||
</resources>
|
||||
|
||||
@@ -141,7 +141,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"للعمل"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"الملف الشخصي للعمل"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"البحث عن تطبيقات العمل هنا"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"يحتوي كل تطبيق للعمل على شارة برتقالية اللون ويظل تحت حماية مؤسستك. يمكنك نقل التطبيقات إلى شاشتك الرئيسية لتسهيل الوصول."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"ملف شخصي للعمل تديره مؤسستك"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"الإشعارات والتطبيقات متوقفة."</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Burada iş tətbiqləri axtarın"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Təşkilatınız tərəfindən idarə olunur"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
|
||||
</resources>
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil za Work"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovde"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Ovim upravlja organizacija"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Obaveštenja i aplikacije su isključeni"</string>
|
||||
</resources>
|
||||
|
||||
@@ -139,7 +139,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Праца"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Працоўны профіль"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Знайдзіце працоўныя праграмы тут"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Кожная працоўная праграма мае аранжавы значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на свой Галоўны экран."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Пад кіраваннем вашай арганізацыі"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Апавяшчэнні і праграмы выключаны"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Служебни"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Служебен потребителски профил"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Тук можете да намерите служебните приложения"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Всяко служебно приложение има оранжева значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Управлява се от организацията ви"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Известията и приложенията са изключени"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"এখানে কাজের অ্যাপ্সগুলি খুঁজুন"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"প্রতিটি কাজের অ্যাপে একটি করে কমলা ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।"</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে"</string>
|
||||
</resources>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
|
||||
<string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
|
||||
<string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
|
||||
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Favoriti"</string>
|
||||
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Omiljeno"</string>
|
||||
<string name="all_apps_button_label" msgid="8130441508702294465">"Spisak aplikacija"</string>
|
||||
<string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
|
||||
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
|
||||
@@ -112,7 +112,7 @@
|
||||
<string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
|
||||
<string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
|
||||
<string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
|
||||
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među favoritima"</string>
|
||||
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string>
|
||||
<string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string>
|
||||
<string name="add_to_folder" msgid="9040534766770853243">"Dodaj u folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
|
||||
<string name="add_to_folder_with_app" msgid="4534929978967147231">"Dodaj u folder sa aplikacijom <xliff:g id="NAME">%1$s</xliff:g>"</string>
|
||||
@@ -138,7 +138,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovdje"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Svaka poslovna aplikacija ima narandžastu značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Feina"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil professional"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Cerca aplicacions per a la feina aquí"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Gestionat per la teva organització"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Les notificacions i les aplicacions estan desactivades"</string>
|
||||
</resources>
|
||||
|
||||
@@ -139,7 +139,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Zde naleznete pracovní aplikace"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Každý pracovní profil má oranžový odznak a je zabezpečen vaší organizací. Aplikace si můžete pro jednoduchost přesunout na plochu."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Spravováno vaší organizací"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Oznámení a aplikace jsou vypnuty"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Administreret af din organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Underretninger og apps er slået fra"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hier findest du Apps für die Arbeit"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Wird von deiner Organisation verwaltet"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Benachrichtigungen und Apps sind deaktiviert"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Εργασίας"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Βρείτε όλες τις εφαρμογές εργασίας εδώ"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Κάθε εφαρμογή εργασίας φέρει ένα πορτοκαλί σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Διαχειριζόμενο από τον οργανισμό σας"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Laborales"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Apps de trabajo"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Administrado por tu organización"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las apps están desactivadas"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Aplicaciones de trabajo"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Administrada por tu organización"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las aplicaciones están desactivadas"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Töörakendused leiate siit"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Igal töörakendusel on oranž märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Haldab teie organisatsioon"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Märguanded ja rakendused on välja lülitatud"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hemen dituzu laneko aplikazioak"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Laneko aplikazio bakoitzak bereizgarri laranja bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Erakundeak kudeatzen du"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"محل کار"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"اینجا برنامههای کاری را پیدا کنید"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"همه برنامههای کاری نشان نارنجیرنگی دارند و توسط سازمان شما امن نگه داشته میشود. برنامههای کاری را برای دسترسی آسانتر به صفحه اصلی انتقال دهید."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"توسط سازمانتان مدیریت میشود"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"اعلانها و برنامهها خاموش هستند"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Etsi työsovelluksia tästä"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Kaikissa työsovelluksissa on oranssi merkki ja ne ovat organisaatiosi suojaamia. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Organisaatiosi hallinnoima"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Ilmoitukset ja sovellukset ovat poissa käytöstä"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Trouvez ici des applications professionnelles"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnelles"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Retrouvez ici vos applications professionnelles"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Buscar aplicacións do traballo aquí"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Perfil xestionado pola túa organización"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"As notificacións e as aplicacións están desactivadas"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"કાર્યાલયની પ્રોફાઇલ"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"કાર્ય ઍપને અહીંથી મેળવો"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"દરેક કાર્ય ઍપ પાસે એક નારંગી બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"તમારી સંસ્થા દ્વારા મેનેજ કરેલ"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफ़ाइल"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"काम से जुड़े सभी ऐप्लिकेशन यहां पाएं"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"काम से जुड़े हर ऐप्लिकेशन पर एक नारंगी रंग का बैज (निशान) होता है जिसकी सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"आपका संगठन प्रबंधित कर रहा है"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"सूचनाएं और ऐप्लिकेशन बंद हैं"</string>
|
||||
</resources>
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ovdje možete pronaći radne aplikacije"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Svaka radna aplikacija ima narančastu značku i štiti je vaša organizacija. Premjestite aplikacije na početni zaslon radi lakšeg pristupa."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Pod upravljanjem vaše organizacije"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Obavijesti i aplikacije isključeni su"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Itt kereshet munkahelyi alkalmazásokat"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Az Ön szervezete kezeli"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Az értesítések és az alkalmazások ki vannak kapcsolva"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Գտեք աշխատանքային հավելվածներ այստեղ"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Աշխատանքային հավելվածները նշված են նարնջագույն նշանով, նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։"</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Կառավարվում է ձեր կազմակերպության կողմից"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Ծանուցումներն ու հավելվածներն անջատված են"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kantor"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temukan aplikasi kerja di sini"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Setiap aplikasi kerja memiliki badge oranye dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Dikelola oleh organisasi"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Notifikasi dan aplikasi nonaktif"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hér finnurðu vinnuforrit"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Ö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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Stjórnað af fyrirtækinu þínu"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Slökkt er á tilkynningum og forritum"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Qui puoi trovare le tue app di lavoro"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"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."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"Gestito dalla tua organizzazione"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"Le notifiche e le app non sono attive"</string>
|
||||
</resources>
|
||||
|
||||
@@ -139,7 +139,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ניתן למצוא כאן את אפליקציות העבודה"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"לכל אפליקציית עבודה יש תג כתום ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן."</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"מנוהל בידי הארגון"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"הודעות ואפליקציות כבויות"</string>
|
||||
</resources>
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
<string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
|
||||
<string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
|
||||
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ここには仕事用アプリが表示されます"</string>
|
||||
<string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"仕事用アプリにはオレンジのバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。"</string>
|
||||
<!-- no translation found for bottom_work_tab_user_education_body (2818107472360579152) -->
|
||||
<skip />
|
||||
<string name="work_mode_on_label" msgid="4781128097185272916">"組織によって管理されています"</string>
|
||||
<string name="work_mode_off_label" msgid="3194894777601421047">"通知とアプリは OFF です"</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user