1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.quickstep; 18 19 import static com.android.launcher3.anim.Interpolators.LINEAR; 20 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; 21 import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber; 22 import static com.android.systemui.shared.recents.utilities.Utilities.getSurface; 23 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; 24 25 import android.animation.ValueAnimator; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageManager; 30 import android.graphics.RectF; 31 import android.os.UserHandle; 32 import android.util.Log; 33 import android.view.Surface; 34 import android.view.View; 35 36 import com.android.launcher3.BaseDraggingActivity; 37 import com.android.launcher3.ItemInfo; 38 import com.android.launcher3.Utilities; 39 import com.android.launcher3.compat.LauncherAppsCompat; 40 import com.android.launcher3.compat.UserManagerCompat; 41 import com.android.launcher3.util.ComponentKey; 42 import com.android.quickstep.util.ClipAnimationHelper; 43 import com.android.quickstep.util.MultiValueUpdateListener; 44 import com.android.quickstep.util.RemoteAnimationTargetSet; 45 import com.android.quickstep.views.RecentsView; 46 import com.android.quickstep.views.TaskView; 47 import com.android.systemui.shared.recents.model.Task; 48 import com.android.systemui.shared.system.RemoteAnimationTargetCompat; 49 50 import java.util.List; 51 52 /** 53 * Contains helpful methods for retrieving data from {@link Task}s. 54 */ 55 public class TaskUtils { 56 57 private static final String TAG = "TaskUtils"; 58 59 /** 60 * TODO: remove this once we switch to getting the icon and label from IconCache. 61 */ 62 public static CharSequence getTitle(Context context, Task task) { 63 LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context); 64 UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context); 65 PackageManager packageManager = context.getPackageManager(); 66 UserHandle user = UserHandle.of(task.key.userId); 67 ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo( 68 task.getTopComponent().getPackageName(), 0, user); 69 if (applicationInfo == null) { 70 Log.e(TAG, "Failed to get title for task " + task); 71 return ""; 72 } 73 return userManagerCompat.getBadgedLabelForUser( 74 applicationInfo.loadLabel(packageManager), user); 75 } 76 77 public static ComponentKey getComponentKeyForTask(Task.TaskKey taskKey) { 78 return new ComponentKey(taskKey.getComponent(), UserHandle.of(taskKey.userId)); 79 } 80 81 82 /** 83 * Try to find a TaskView that corresponds with the component of the launched view. 84 * 85 * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation. 86 * Otherwise, we will assume we are using a normal app transition, but it's possible that the 87 * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView. 88 */ 89 public static TaskView findTaskViewToLaunch( 90 BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { 91 if (v instanceof TaskView) { 92 return (TaskView) v; 93 } 94 RecentsView recentsView = activity.getOverviewPanel(); 95 96 // It's possible that the launched view can still be resolved to a visible task view, check 97 // the task id of the opening task and see if we can find a match. 98 if (v.getTag() instanceof ItemInfo) { 99 ItemInfo itemInfo = (ItemInfo) v.getTag(); 100 ComponentName componentName = itemInfo.getTargetComponent(); 101 int userId = itemInfo.user.getIdentifier(); 102 if (componentName != null) { 103 for (int i = 0; i < recentsView.getChildCount(); i++) { 104 TaskView taskView = recentsView.getPageAt(i); 105 if (recentsView.isTaskViewVisible(taskView)) { 106 Task.TaskKey key = taskView.getTask().key; 107 if (componentName.equals(key.getComponent()) && userId == key.userId) { 108 return taskView; 109 } 110 } 111 } 112 } 113 } 114 115 if (targets == null) { 116 return null; 117 } 118 // Resolve the opening task id 119 int openingTaskId = -1; 120 for (RemoteAnimationTargetCompat target : targets) { 121 if (target.mode == MODE_OPENING) { 122 openingTaskId = target.taskId; 123 break; 124 } 125 } 126 127 // If there is no opening task id, fall back to the normal app icon launch animation 128 if (openingTaskId == -1) { 129 return null; 130 } 131 132 // If the opening task id is not currently visible in overview, then fall back to normal app 133 // icon launch animation 134 TaskView taskView = recentsView.getTaskView(openingTaskId); 135 if (taskView == null || !recentsView.isTaskViewVisible(taskView)) { 136 return null; 137 } 138 return taskView; 139 } 140 141 /** 142 * @return Animator that controls the window of the opening targets for the recents launch 143 * animation. 144 */ 145 public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges, 146 RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) { 147 final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); 148 appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 149 appAnimator.addUpdateListener(new MultiValueUpdateListener() { 150 151 // Defer fading out the view until after the app window gets faded in 152 final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR); 153 final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR); 154 155 final RemoteAnimationTargetSet mTargetSet; 156 157 final RectF mThumbnailRect; 158 private Surface mSurface; 159 private long mFrameNumber; 160 161 { 162 mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING); 163 inOutHelper.setTaskTransformCallback((t, app) -> { 164 t.setAlpha(app.leash, mTaskAlpha.value); 165 166 if (!skipViewChanges) { 167 t.deferTransactionUntil(app.leash, mSurface, mFrameNumber); 168 } 169 }); 170 171 inOutHelper.prepareAnimation(true /* isOpening */); 172 inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), 173 mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]); 174 175 mThumbnailRect = new RectF(inOutHelper.getTargetRect()); 176 mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY()); 177 Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX()); 178 } 179 180 @Override 181 public void onUpdate(float percent) { 182 mSurface = getSurface(v); 183 mFrameNumber = mSurface != null ? getNextFrameNumber(mSurface) : -1; 184 if (mFrameNumber == -1) { 185 // Booo, not cool! Our surface got destroyed, so no reason to animate anything. 186 Log.w(TAG, "Failed to animate, surface got destroyed."); 187 return; 188 } 189 190 RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent); 191 if (!skipViewChanges) { 192 float scale = taskBounds.width() / mThumbnailRect.width(); 193 v.setScaleX(scale); 194 v.setScaleY(scale); 195 v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX()); 196 v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY()); 197 v.setAlpha(mViewAlpha.value); 198 } 199 } 200 }); 201 return appAnimator; 202 } 203 204 public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets, 205 int taskId, int mode) { 206 for (RemoteAnimationTargetCompat target : targets) { 207 if (target.mode == mode && target.taskId == taskId) { 208 return true; 209 } 210 } 211 return false; 212 } 213 214 public static boolean checkCurrentOrManagedUserId(int currentUserId, Context context) { 215 if (currentUserId == UserHandle.myUserId()) { 216 return true; 217 } 218 List<UserHandle> allUsers = UserManagerCompat.getInstance(context).getUserProfiles(); 219 for (int i = allUsers.size() - 1; i >= 0; i--) { 220 if (currentUserId == allUsers.get(i).getIdentifier()) { 221 return true; 222 } 223 } 224 return false; 225 } 226 } 227