1 /* 2 * Copyright (C) 2017 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 package com.android.quickstep; 17 18 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; 19 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; 20 21 import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; 22 import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION; 23 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator; 24 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; 25 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; 26 27 import android.animation.Animator; 28 import android.animation.AnimatorListenerAdapter; 29 import android.animation.AnimatorSet; 30 import android.app.ActivityOptions; 31 import android.content.Intent; 32 import android.content.res.Configuration; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.view.View; 37 38 import com.android.launcher3.AbstractFloatingView; 39 import com.android.launcher3.BaseDraggingActivity; 40 import com.android.launcher3.DeviceProfile; 41 import com.android.launcher3.InvariantDeviceProfile; 42 import com.android.launcher3.ItemInfo; 43 import com.android.launcher3.LauncherAnimationRunner; 44 import com.android.launcher3.LauncherAppState; 45 import com.android.launcher3.R; 46 import com.android.launcher3.anim.Interpolators; 47 import com.android.launcher3.badge.BadgeInfo; 48 import com.android.launcher3.uioverrides.UiFactory; 49 import com.android.launcher3.util.SystemUiController; 50 import com.android.launcher3.util.Themes; 51 import com.android.launcher3.views.BaseDragLayer; 52 import com.android.quickstep.fallback.FallbackRecentsView; 53 import com.android.quickstep.fallback.RecentsRootView; 54 import com.android.quickstep.util.ClipAnimationHelper; 55 import com.android.quickstep.views.RecentsViewContainer; 56 import com.android.quickstep.views.TaskView; 57 import com.android.systemui.shared.system.ActivityOptionsCompat; 58 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; 59 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; 60 import com.android.systemui.shared.system.RemoteAnimationTargetCompat; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 65 /** 66 * A simple activity to show the recently launched tasks 67 */ 68 public class RecentsActivity extends BaseDraggingActivity { 69 70 private Handler mUiHandler = new Handler(Looper.getMainLooper()); 71 private RecentsRootView mRecentsRootView; 72 private FallbackRecentsView mFallbackRecentsView; 73 private RecentsViewContainer mOverviewPanelContainer; 74 75 private Configuration mOldConfig; 76 77 @Override 78 protected void onCreate(Bundle savedInstanceState) { 79 super.onCreate(savedInstanceState); 80 81 mOldConfig = new Configuration(getResources().getConfiguration()); 82 initDeviceProfile(); 83 84 setContentView(R.layout.fallback_recents_activity); 85 mRecentsRootView = findViewById(R.id.drag_layer); 86 mFallbackRecentsView = findViewById(R.id.overview_panel); 87 mOverviewPanelContainer = findViewById(R.id.overview_panel_container); 88 89 mRecentsRootView.setup(); 90 91 getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, 92 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); 93 RecentsActivityTracker.onRecentsActivityCreate(this); 94 } 95 96 @Override 97 public void onConfigurationChanged(Configuration newConfig) { 98 int diff = newConfig.diff(mOldConfig); 99 if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { 100 onHandleConfigChanged(); 101 } 102 mOldConfig.setTo(newConfig); 103 super.onConfigurationChanged(newConfig); 104 } 105 106 @Override 107 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 108 onHandleConfigChanged(); 109 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 110 } 111 112 public void onRootViewSizeChanged() { 113 if (isInMultiWindowModeCompat()) { 114 onHandleConfigChanged(); 115 } 116 } 117 118 private void onHandleConfigChanged() { 119 mUserEventDispatcher = null; 120 initDeviceProfile(); 121 122 AbstractFloatingView.closeOpenViews(this, true, 123 AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); 124 dispatchDeviceProfileChanged(); 125 126 mRecentsRootView.setup(); 127 reapplyUi(); 128 } 129 130 @Override 131 protected void reapplyUi() { 132 mRecentsRootView.dispatchInsets(); 133 } 134 135 private void initDeviceProfile() { 136 // In case we are reusing IDP, create a copy so that we dont conflict with Launcher 137 // activity. 138 LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); 139 if (isInMultiWindowModeCompat()) { 140 InvariantDeviceProfile idp = appState == null 141 ? new InvariantDeviceProfile(this) : appState.getInvariantDeviceProfile(); 142 DeviceProfile dp = idp.getDeviceProfile(this); 143 mDeviceProfile = mRecentsRootView == null ? dp.copy(this) 144 : dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize()); 145 } else { 146 // If we are reusing the Invariant device profile, make a copy. 147 mDeviceProfile = appState == null 148 ? new InvariantDeviceProfile(this).getDeviceProfile(this) 149 : appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this); 150 } 151 onDeviceProfileInitiated(); 152 } 153 154 @Override 155 public BaseDragLayer getDragLayer() { 156 return mRecentsRootView; 157 } 158 159 @Override 160 public View getRootView() { 161 return mRecentsRootView; 162 } 163 164 @Override 165 public <T extends View> T getOverviewPanel() { 166 return (T) mFallbackRecentsView; 167 } 168 169 public RecentsViewContainer getOverviewPanelContainer() { 170 return mOverviewPanelContainer; 171 } 172 173 @Override 174 public BadgeInfo getBadgeInfoForItem(ItemInfo info) { 175 return null; 176 } 177 178 @Override 179 public ActivityOptions getActivityLaunchOptions(final View v) { 180 if (!(v instanceof TaskView)) { 181 return null; 182 } 183 184 final TaskView taskView = (TaskView) v; 185 RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler, 186 true /* startAtFrontOfQueue */) { 187 188 @Override 189 public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, 190 AnimationResult result) { 191 result.setAnimation(composeRecentsLaunchAnimator(taskView, targetCompats)); 192 } 193 }; 194 return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( 195 runner, RECENTS_LAUNCH_DURATION, 196 RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION)); 197 } 198 199 /** 200 * Composes the animations for a launch from the recents list if possible. 201 */ 202 private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView, 203 RemoteAnimationTargetCompat[] targets) { 204 AnimatorSet target = new AnimatorSet(); 205 boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING); 206 ClipAnimationHelper helper = new ClipAnimationHelper(); 207 target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper) 208 .setDuration(RECENTS_LAUNCH_DURATION)); 209 210 // Found a visible recents task that matches the opening app, lets launch the app from there 211 if (activityClosing) { 212 Animator adjacentAnimation = mFallbackRecentsView 213 .createAdjacentPageAnimForTaskLaunch(taskView, helper); 214 adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); 215 adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION); 216 adjacentAnimation.addListener(new AnimatorListenerAdapter() { 217 @Override 218 public void onAnimationEnd(Animator animation) { 219 mFallbackRecentsView.resetTaskVisuals(); 220 } 221 }); 222 target.play(adjacentAnimation); 223 } 224 return target; 225 } 226 227 @Override 228 public void invalidateParent(ItemInfo info) { } 229 230 @Override 231 protected void onStart() { 232 // Set the alpha to 1 before calling super, as it may get set back to 0 due to 233 // onActivityStart callback. 234 mFallbackRecentsView.setContentAlpha(1); 235 super.onStart(); 236 UiFactory.onStart(this); 237 mFallbackRecentsView.resetTaskVisuals(); 238 } 239 240 @Override 241 protected void onStop() { 242 super.onStop(); 243 244 // Workaround for b/78520668, explicitly trim memory once UI is hidden 245 onTrimMemory(TRIM_MEMORY_UI_HIDDEN); 246 } 247 248 @Override 249 public void onTrimMemory(int level) { 250 super.onTrimMemory(level); 251 UiFactory.onTrimMemory(this, level); 252 } 253 254 @Override 255 protected void onNewIntent(Intent intent) { 256 super.onNewIntent(intent); 257 RecentsActivityTracker.onRecentsActivityNewIntent(this); 258 } 259 260 @Override 261 protected void onDestroy() { 262 super.onDestroy(); 263 RecentsActivityTracker.onRecentsActivityDestroy(this); 264 } 265 266 @Override 267 public void onBackPressed() { 268 // TODO: Launch the task we came from 269 startHome(); 270 } 271 272 public void startHome() { 273 startActivity(new Intent(Intent.ACTION_MAIN) 274 .addCategory(Intent.CATEGORY_HOME) 275 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 276 } 277 278 @Override 279 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 280 super.dump(prefix, fd, writer, args); 281 writer.println(prefix + "Misc:"); 282 dumpMisc(writer); 283 } 284 } 285