1 /* 2 * Copyright (C) 2008 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.launcher3; 18 19 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; 20 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; 21 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; 22 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO; 23 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 24 25 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; 26 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; 27 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; 28 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; 29 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; 30 import static com.android.launcher3.LauncherState.ALL_APPS; 31 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; 32 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; 33 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE; 34 import static com.android.launcher3.LauncherState.NORMAL; 35 import static com.android.launcher3.LauncherState.NO_OFFSET; 36 import static com.android.launcher3.LauncherState.NO_SCALE; 37 import static com.android.launcher3.LauncherState.SPRING_LOADED; 38 import static com.android.launcher3.Utilities.postAsyncCallback; 39 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions; 40 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD; 41 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; 42 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; 43 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY; 44 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH; 45 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT; 46 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME; 47 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP; 48 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED; 49 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; 50 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; 51 import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING; 52 import static com.android.launcher3.popup.SystemShortcut.APP_INFO; 53 import static com.android.launcher3.popup.SystemShortcut.INSTALL; 54 import static com.android.launcher3.popup.SystemShortcut.WIDGETS; 55 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; 56 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; 57 58 import android.animation.Animator; 59 import android.animation.AnimatorListenerAdapter; 60 import android.animation.AnimatorSet; 61 import android.animation.ObjectAnimator; 62 import android.animation.ValueAnimator; 63 import android.annotation.TargetApi; 64 import android.app.Notification; 65 import android.app.NotificationChannel; 66 import android.app.NotificationManager; 67 import android.app.PendingIntent; 68 import android.appwidget.AppWidgetHostView; 69 import android.appwidget.AppWidgetManager; 70 import android.content.ActivityNotFoundException; 71 import android.content.BroadcastReceiver; 72 import android.content.ComponentCallbacks2; 73 import android.content.Context; 74 import android.content.Intent; 75 import android.content.IntentFilter; 76 import android.content.IntentSender; 77 import android.content.SharedPreferences; 78 import android.content.pm.PackageManager; 79 import android.content.res.Configuration; 80 import android.database.sqlite.SQLiteDatabase; 81 import android.graphics.Bitmap; 82 import android.graphics.Rect; 83 import android.os.Build; 84 import android.os.Bundle; 85 import android.os.CancellationSignal; 86 import android.os.Parcelable; 87 import android.os.Process; 88 import android.os.StrictMode; 89 import android.os.SystemClock; 90 import android.text.TextUtils; 91 import android.text.method.TextKeyListener; 92 import android.util.Log; 93 import android.util.SparseArray; 94 import android.view.KeyEvent; 95 import android.view.KeyboardShortcutGroup; 96 import android.view.KeyboardShortcutInfo; 97 import android.view.LayoutInflater; 98 import android.view.Menu; 99 import android.view.MotionEvent; 100 import android.view.View; 101 import android.view.ViewGroup; 102 import android.view.WindowManager.LayoutParams; 103 import android.view.accessibility.AccessibilityEvent; 104 import android.view.animation.OvershootInterpolator; 105 import android.widget.ImageView; 106 import android.widget.Toast; 107 108 import androidx.annotation.CallSuper; 109 import androidx.annotation.Nullable; 110 import androidx.annotation.StringRes; 111 import androidx.annotation.VisibleForTesting; 112 113 import com.android.launcher3.DropTarget.DragObject; 114 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; 115 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction; 116 import com.android.launcher3.allapps.AllAppsContainerView; 117 import com.android.launcher3.allapps.AllAppsStore; 118 import com.android.launcher3.allapps.AllAppsTransitionController; 119 import com.android.launcher3.allapps.DiscoveryBounce; 120 import com.android.launcher3.anim.PropertyListBuilder; 121 import com.android.launcher3.compat.AccessibilityManagerCompat; 122 import com.android.launcher3.config.FeatureFlags; 123 import com.android.launcher3.dot.DotInfo; 124 import com.android.launcher3.dragndrop.DragController; 125 import com.android.launcher3.dragndrop.DragLayer; 126 import com.android.launcher3.dragndrop.DragOptions; 127 import com.android.launcher3.dragndrop.DragView; 128 import com.android.launcher3.dragndrop.LauncherDragController; 129 import com.android.launcher3.folder.FolderGridOrganizer; 130 import com.android.launcher3.folder.FolderIcon; 131 import com.android.launcher3.icons.BitmapRenderer; 132 import com.android.launcher3.icons.IconCache; 133 import com.android.launcher3.keyboard.ViewGroupFocusHelper; 134 import com.android.launcher3.logger.LauncherAtom; 135 import com.android.launcher3.logging.FileLog; 136 import com.android.launcher3.logging.InstanceId; 137 import com.android.launcher3.logging.InstanceIdSequence; 138 import com.android.launcher3.logging.StatsLogManager; 139 import com.android.launcher3.model.BgDataModel.Callbacks; 140 import com.android.launcher3.model.ItemInstallQueue; 141 import com.android.launcher3.model.ModelUtils; 142 import com.android.launcher3.model.ModelWriter; 143 import com.android.launcher3.model.WidgetsModel; 144 import com.android.launcher3.model.data.AppInfo; 145 import com.android.launcher3.model.data.FolderInfo; 146 import com.android.launcher3.model.data.ItemInfo; 147 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 148 import com.android.launcher3.model.data.WorkspaceItemInfo; 149 import com.android.launcher3.notification.NotificationListener; 150 import com.android.launcher3.pm.PinRequestHelper; 151 import com.android.launcher3.pm.UserCache; 152 import com.android.launcher3.popup.PopupContainerWithArrow; 153 import com.android.launcher3.popup.PopupDataProvider; 154 import com.android.launcher3.popup.SystemShortcut; 155 import com.android.launcher3.qsb.QsbContainerView; 156 import com.android.launcher3.statemanager.StateManager; 157 import com.android.launcher3.statemanager.StateManager.StateHandler; 158 import com.android.launcher3.statemanager.StatefulActivity; 159 import com.android.launcher3.states.RotationHelper; 160 import com.android.launcher3.testing.TestLogging; 161 import com.android.launcher3.testing.TestProtocol; 162 import com.android.launcher3.touch.AllAppsSwipeController; 163 import com.android.launcher3.touch.ItemClickHandler; 164 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; 165 import com.android.launcher3.util.ActivityResultInfo; 166 import com.android.launcher3.util.ActivityTracker; 167 import com.android.launcher3.util.ComponentKey; 168 import com.android.launcher3.util.IntArray; 169 import com.android.launcher3.util.ItemInfoMatcher; 170 import com.android.launcher3.util.MultiValueAlpha; 171 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; 172 import com.android.launcher3.util.OnboardingPrefs; 173 import com.android.launcher3.util.PackageManagerHelper; 174 import com.android.launcher3.util.PackageUserKey; 175 import com.android.launcher3.util.PendingRequestArgs; 176 import com.android.launcher3.util.SafeCloseable; 177 import com.android.launcher3.util.SystemUiController; 178 import com.android.launcher3.util.Themes; 179 import com.android.launcher3.util.Thunk; 180 import com.android.launcher3.util.TouchController; 181 import com.android.launcher3.util.TraceHelper; 182 import com.android.launcher3.util.UiThreadHelper; 183 import com.android.launcher3.util.ViewOnDrawExecutor; 184 import com.android.launcher3.views.ActivityContext; 185 import com.android.launcher3.views.FloatingSurfaceView; 186 import com.android.launcher3.views.OptionsPopupView; 187 import com.android.launcher3.views.ScrimView; 188 import com.android.launcher3.widget.LauncherAppWidgetHost; 189 import com.android.launcher3.widget.LauncherAppWidgetHostView; 190 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; 191 import com.android.launcher3.widget.PendingAddShortcutInfo; 192 import com.android.launcher3.widget.PendingAddWidgetInfo; 193 import com.android.launcher3.widget.PendingAppWidgetHostView; 194 import com.android.launcher3.widget.WidgetAddFlowHandler; 195 import com.android.launcher3.widget.WidgetManagerHelper; 196 import com.android.launcher3.widget.custom.CustomWidgetManager; 197 import com.android.launcher3.widget.model.WidgetsListBaseEntry; 198 import com.android.launcher3.widget.picker.WidgetsFullSheet; 199 import com.android.systemui.plugins.OverlayPlugin; 200 import com.android.systemui.plugins.PluginListener; 201 import com.android.systemui.plugins.shared.LauncherExterns; 202 import com.android.systemui.plugins.shared.LauncherOverlayManager; 203 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; 204 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; 205 206 import java.io.FileDescriptor; 207 import java.io.PrintWriter; 208 import java.util.ArrayList; 209 import java.util.Collection; 210 import java.util.HashMap; 211 import java.util.HashSet; 212 import java.util.List; 213 import java.util.function.Predicate; 214 import java.util.function.Supplier; 215 import java.util.stream.Stream; 216 217 /** 218 * Default launcher application. 219 */ 220 public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns, 221 Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> { 222 public static final String TAG = "Launcher"; 223 224 public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>(); 225 226 static final boolean LOGD = false; 227 228 static final boolean DEBUG_STRICT_MODE = false; 229 230 private static final int REQUEST_CREATE_SHORTCUT = 1; 231 private static final int REQUEST_CREATE_APPWIDGET = 5; 232 233 private static final int REQUEST_PICK_APPWIDGET = 9; 234 235 private static final int REQUEST_BIND_APPWIDGET = 11; 236 public static final int REQUEST_BIND_PENDING_APPWIDGET = 12; 237 public static final int REQUEST_RECONFIGURE_APPWIDGET = 13; 238 239 private static final int REQUEST_PERMISSION_CALL_PHONE = 14; 240 241 private static final float BOUNCE_ANIMATION_TENSION = 1.3f; 242 243 /** 244 * IntentStarter uses request codes starting with this. This must be greater than all activity 245 * request codes used internally. 246 */ 247 protected static final int REQUEST_LAST = 100; 248 249 // Type: int 250 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 251 // Type: int 252 private static final String RUNTIME_STATE = "launcher.state"; 253 // Type: PendingRequestArgs 254 private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args"; 255 // Type: int 256 private static final String RUNTIME_STATE_PENDING_REQUEST_CODE = "launcher.request_code"; 257 // Type: ActivityResultInfo 258 private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result"; 259 // Type: SparseArray<Parcelable> 260 private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; 261 262 public static final String ON_CREATE_EVT = "Launcher.onCreate"; 263 public static final String ON_START_EVT = "Launcher.onStart"; 264 public static final String ON_RESUME_EVT = "Launcher.onResume"; 265 public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent"; 266 267 private StateManager<LauncherState> mStateManager; 268 269 private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; 270 271 // How long to wait before the new-shortcut animation automatically pans the workspace 272 @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500; 273 private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; 274 @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500; 275 276 private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375; 277 278 private Configuration mOldConfig; 279 280 @Thunk 281 Workspace mWorkspace; 282 @Thunk 283 DragLayer mDragLayer; 284 private DragController mDragController; 285 286 private WidgetManagerHelper mAppWidgetManager; 287 private LauncherAppWidgetHost mAppWidgetHost; 288 289 private final int[] mTmpAddItemCellCoordinates = new int[2]; 290 291 @Thunk 292 Hotseat mHotseat; 293 294 private DropTargetBar mDropTargetBar; 295 296 // Main container view for the all apps screen. 297 @Thunk 298 AllAppsContainerView mAppsView; 299 AllAppsTransitionController mAllAppsController; 300 301 // Scrim view for the all apps and overview state. 302 @Thunk 303 ScrimView mScrimView; 304 305 // UI and state for the overview panel 306 private View mOverviewPanel; 307 308 @Thunk 309 boolean mWorkspaceLoading = true; 310 311 // Used to notify when an activity launch has been deferred because launcher is not yet resumed 312 // TODO: See if we can remove this later 313 private Runnable mOnDeferredActivityLaunchCallback; 314 315 private ViewOnDrawExecutor mPendingExecutor; 316 317 private LauncherModel mModel; 318 private ModelWriter mModelWriter; 319 private IconCache mIconCache; 320 private LauncherAccessibilityDelegate mAccessibilityDelegate; 321 322 private PopupDataProvider mPopupDataProvider; 323 324 private int mSynchronouslyBoundPage = PagedView.INVALID_PAGE; 325 private int mPageToBindSynchronously = PagedView.INVALID_PAGE; 326 327 // We only want to get the SharedPreferences once since it does an FS stat each time we get 328 // it from the context. 329 private SharedPreferences mSharedPrefs; 330 private OnboardingPrefs mOnboardingPrefs; 331 332 // Activity result which needs to be processed after workspace has loaded. 333 private ActivityResultInfo mPendingActivityResult; 334 /** 335 * Holds extra information required to handle a result from an external call, like 336 * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)} 337 */ 338 private PendingRequestArgs mPendingRequestArgs; 339 // Request id for any pending activity result 340 protected int mPendingActivityRequestCode = -1; 341 342 private ViewGroupFocusHelper mFocusHandler; 343 344 private RotationHelper mRotationHelper; 345 346 protected LauncherOverlayManager mOverlayManager; 347 // If true, overlay callbacks are deferred 348 private boolean mDeferOverlayCallbacks; 349 private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred; 350 351 protected long mLastTouchUpTime = -1; 352 private boolean mTouchInProgress; 353 354 private SafeCloseable mUserChangedCallbackCloseable; 355 356 // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. 357 // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. 358 // User actions within AllApps state are logged with this InstanceId, to recreate AllApps 359 // session on the server side. 360 protected InstanceId mAllAppsSessionLogId; 361 private LauncherState mPrevLauncherState; 362 363 @Override onCreate(Bundle savedInstanceState)364 protected void onCreate(Bundle savedInstanceState) { 365 Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT, 366 TraceHelper.FLAG_UI_EVENT); 367 if (DEBUG_STRICT_MODE) { 368 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 369 .detectDiskReads() 370 .detectDiskWrites() 371 .detectNetwork() // or .detectAll() for all detectable problems 372 .penaltyLog() 373 .build()); 374 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 375 .detectLeakedSqlLiteObjects() 376 .detectLeakedClosableObjects() 377 .penaltyLog() 378 .penaltyDeath() 379 .build()); 380 } 381 382 if (Utilities.IS_DEBUG_DEVICE && FeatureFlags.NOTIFY_CRASHES.get()) { 383 final String notificationChannelId = "com.android.launcher3.Debug"; 384 final String notificationChannelName = "Debug"; 385 final String notificationTag = "Debug"; 386 final int notificationId = 0; 387 388 NotificationManager notificationManager = getSystemService(NotificationManager.class); 389 notificationManager.createNotificationChannel(new NotificationChannel( 390 notificationChannelId, notificationChannelName, 391 NotificationManager.IMPORTANCE_HIGH)); 392 393 Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> { 394 String stackTrace = Log.getStackTraceString(throwable); 395 396 Intent shareIntent = new Intent(Intent.ACTION_SEND); 397 shareIntent.setType("text/plain"); 398 shareIntent.putExtra(Intent.EXTRA_TEXT, stackTrace); 399 shareIntent = Intent.createChooser(shareIntent, null); 400 PendingIntent sharePendingIntent = PendingIntent.getActivity( 401 this, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT 402 ); 403 404 Notification notification = new Notification.Builder(this, notificationChannelId) 405 .setSmallIcon(android.R.drawable.ic_menu_close_clear_cancel) 406 .setContentTitle("Launcher crash detected!") 407 .setStyle(new Notification.BigTextStyle().bigText(stackTrace)) 408 .addAction(android.R.drawable.ic_menu_share, "Share", sharePendingIntent) 409 .build(); 410 notificationManager.notify(notificationTag, notificationId, notification); 411 412 Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = 413 Thread.getDefaultUncaughtExceptionHandler(); 414 if (defaultUncaughtExceptionHandler != null) { 415 defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); 416 } 417 }); 418 } 419 420 super.onCreate(savedInstanceState); 421 422 LauncherAppState app = LauncherAppState.getInstance(this); 423 mOldConfig = new Configuration(getResources().getConfiguration()); 424 mModel = app.getModel(); 425 426 mRotationHelper = new RotationHelper(this); 427 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); 428 initDeviceProfile(idp); 429 idp.addOnChangeListener(this); 430 mSharedPrefs = Utilities.getPrefs(this); 431 mIconCache = app.getIconCache(); 432 mAccessibilityDelegate = createAccessibilityDelegate(); 433 434 mDragController = new LauncherDragController(this); 435 mAllAppsController = new AllAppsTransitionController(this); 436 mStateManager = new StateManager<>(this, NORMAL); 437 438 mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); 439 440 mAppWidgetManager = new WidgetManagerHelper(this); 441 mAppWidgetHost = createAppWidgetHost(); 442 mAppWidgetHost.startListening(); 443 444 inflateRootView(R.layout.launcher); 445 setupViews(); 446 crossFadeWithPreviousAppearance(); 447 mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots); 448 449 boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this); 450 if (internalStateHandled) { 451 if (savedInstanceState != null) { 452 // InternalStateHandler has already set the appropriate state. 453 // We dont need to do anything. 454 savedInstanceState.remove(RUNTIME_STATE); 455 } 456 } 457 restoreState(savedInstanceState); 458 mStateManager.reapplyState(); 459 460 // We only load the page synchronously if the user rotates (or triggers a 461 // configuration change) while launcher is in the foreground 462 int currentScreen = PagedView.INVALID_PAGE; 463 if (savedInstanceState != null) { 464 currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen); 465 } 466 mPageToBindSynchronously = currentScreen; 467 468 if (!mModel.addCallbacksAndLoad(this)) { 469 if (!internalStateHandled) { 470 // If we are not binding synchronously, show a fade in animation when 471 // the first page bind completes. 472 mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0); 473 } 474 } 475 476 // For handling default keys 477 setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); 478 479 setContentView(getRootView()); 480 getRootView().dispatchInsets(); 481 482 // Listen for broadcasts 483 registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 484 485 getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, 486 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); 487 488 if (mLauncherCallbacks != null) { 489 mLauncherCallbacks.onCreate(savedInstanceState); 490 } 491 mOverlayManager = getDefaultOverlay(); 492 PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this, 493 OverlayPlugin.class, false /* allowedMultiple */); 494 495 mRotationHelper.initialize(); 496 TraceHelper.INSTANCE.endSection(traceToken); 497 498 mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( 499 () -> getStateManager().goToState(NORMAL)); 500 501 if (Utilities.ATLEAST_R) { 502 getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); 503 } 504 } 505 getDefaultOverlay()506 protected LauncherOverlayManager getDefaultOverlay() { 507 return new LauncherOverlayManager() { }; 508 } 509 createOnboardingPrefs(SharedPreferences sharedPrefs)510 protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) { 511 return new OnboardingPrefs<>(this, sharedPrefs); 512 } 513 getOnboardingPrefs()514 public OnboardingPrefs getOnboardingPrefs() { 515 return mOnboardingPrefs; 516 } 517 518 @Override onPluginConnected(OverlayPlugin overlayManager, Context context)519 public void onPluginConnected(OverlayPlugin overlayManager, Context context) { 520 switchOverlay(() -> overlayManager.createOverlayManager(this, this)); 521 } 522 523 @Override onPluginDisconnected(OverlayPlugin plugin)524 public void onPluginDisconnected(OverlayPlugin plugin) { 525 switchOverlay(this::getDefaultOverlay); 526 } 527 switchOverlay(Supplier<LauncherOverlayManager> overlaySupplier)528 private void switchOverlay(Supplier<LauncherOverlayManager> overlaySupplier) { 529 if (mOverlayManager != null) { 530 mOverlayManager.onActivityDestroyed(this); 531 } 532 mOverlayManager = overlaySupplier.get(); 533 if (getRootView().isAttachedToWindow()) { 534 mOverlayManager.onAttachedToWindow(); 535 } 536 mDeferOverlayCallbacks = true; 537 checkIfOverlayStillDeferred(); 538 } 539 540 @Override dispatchDeviceProfileChanged()541 protected void dispatchDeviceProfileChanged() { 542 super.dispatchDeviceProfileChanged(); 543 mOverlayManager.onDeviceProvideChanged(); 544 } 545 546 @Override onEnterAnimationComplete()547 public void onEnterAnimationComplete() { 548 super.onEnterAnimationComplete(); 549 mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE); 550 AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE); 551 } 552 553 @Override onConfigurationChanged(Configuration newConfig)554 public void onConfigurationChanged(Configuration newConfig) { 555 int diff = newConfig.diff(mOldConfig); 556 if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { 557 onIdpChanged(mDeviceProfile.inv); 558 } 559 560 mOldConfig.setTo(newConfig); 561 super.onConfigurationChanged(newConfig); 562 } 563 564 @Override onIdpChanged(InvariantDeviceProfile idp)565 public void onIdpChanged(InvariantDeviceProfile idp) { 566 initDeviceProfile(idp); 567 dispatchDeviceProfileChanged(); 568 reapplyUi(); 569 mDragLayer.recreateControllers(); 570 571 // Calling onSaveInstanceState ensures that static cache used by listWidgets is 572 // initialized properly. 573 onSaveInstanceState(new Bundle()); 574 mModel.rebindCallbacks(); 575 } 576 onAssistantVisibilityChanged(float visibility)577 public void onAssistantVisibilityChanged(float visibility) { 578 mHotseat.getQsb().setAlpha(1f - visibility); 579 } 580 581 /** 582 * Called when one handed mode activated and deactivated. 583 * @param activated true if one handed mode activated, false otherwise. 584 */ onOneHandedStateChanged(boolean activated)585 public void onOneHandedStateChanged(boolean activated) { 586 mDragLayer.onOneHandedModeStateChanged(activated); 587 } 588 initDeviceProfile(InvariantDeviceProfile idp)589 private void initDeviceProfile(InvariantDeviceProfile idp) { 590 // Load configuration-specific DeviceProfile 591 mDeviceProfile = idp.getDeviceProfile(this); 592 if (isInMultiWindowMode()) { 593 mDeviceProfile = mDeviceProfile.getMultiWindowProfile( 594 this, getMultiWindowDisplaySize()); 595 } 596 597 onDeviceProfileInitiated(); 598 mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true); 599 } 600 getRotationHelper()601 public RotationHelper getRotationHelper() { 602 return mRotationHelper; 603 } 604 getFocusHandler()605 public ViewGroupFocusHelper getFocusHandler() { 606 return mFocusHandler; 607 } 608 609 @Override getStateManager()610 public StateManager<LauncherState> getStateManager() { 611 return mStateManager; 612 } 613 614 private LauncherCallbacks mLauncherCallbacks; 615 616 /** 617 * Call this after onCreate to set or clear overlay. 618 */ 619 @Override setLauncherOverlay(LauncherOverlay overlay)620 public void setLauncherOverlay(LauncherOverlay overlay) { 621 if (overlay != null) { 622 overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl()); 623 } 624 mWorkspace.setLauncherOverlay(overlay); 625 } 626 627 @Override runOnOverlayHidden(Runnable runnable)628 public void runOnOverlayHidden(Runnable runnable) { 629 getWorkspace().runOnOverlayHidden(runnable); 630 } 631 setLauncherCallbacks(LauncherCallbacks callbacks)632 public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { 633 mLauncherCallbacks = callbacks; 634 return true; 635 } 636 isDraggingEnabled()637 public boolean isDraggingEnabled() { 638 // We prevent dragging when we are loading the workspace as it is possible to pick up a view 639 // that is subsequently removed from the workspace in startBinding(). 640 return !isWorkspaceLoading(); 641 } 642 getPopupDataProvider()643 public PopupDataProvider getPopupDataProvider() { 644 return mPopupDataProvider; 645 } 646 647 @Override getDotInfoForItem(ItemInfo info)648 public DotInfo getDotInfoForItem(ItemInfo info) { 649 return mPopupDataProvider.getDotInfoForItem(info); 650 } 651 652 @Override invalidateParent(ItemInfo info)653 public void invalidateParent(ItemInfo info) { 654 if (info.container >= 0) { 655 View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); 656 if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) { 657 if (new FolderGridOrganizer(getDeviceProfile().inv) 658 .setFolderInfo((FolderInfo) folderIcon.getTag()) 659 .isItemInPreview(info.rank)) { 660 folderIcon.invalidate(); 661 } 662 } 663 } 664 } 665 666 /** 667 * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have 668 * a configuration step, this allows the proper animations to run after other transitions. 669 */ completeAdd( int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info)670 private int completeAdd( 671 int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) { 672 int screenId = info.screenId; 673 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 674 // When the screen id represents an actual screen (as opposed to a rank) we make sure 675 // that the drop page actually exists. 676 screenId = ensurePendingDropLayoutExists(info.screenId); 677 } 678 679 switch (requestCode) { 680 case REQUEST_CREATE_SHORTCUT: 681 completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info); 682 announceForAccessibility(R.string.item_added_to_workspace); 683 break; 684 case REQUEST_CREATE_APPWIDGET: 685 completeAddAppWidget(appWidgetId, info, null, null); 686 break; 687 case REQUEST_RECONFIGURE_APPWIDGET: 688 mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED); 689 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED); 690 break; 691 case REQUEST_BIND_PENDING_APPWIDGET: { 692 int widgetId = appWidgetId; 693 LauncherAppWidgetInfo widgetInfo = 694 completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY); 695 if (widgetInfo != null) { 696 // Since the view was just bound, also launch the configure activity if needed 697 LauncherAppWidgetProviderInfo provider = mAppWidgetManager 698 .getLauncherAppWidgetInfo(widgetId); 699 if (provider != null) { 700 new WidgetAddFlowHandler(provider) 701 .startConfigActivity(this, widgetInfo, 702 REQUEST_RECONFIGURE_APPWIDGET); 703 } 704 } 705 break; 706 } 707 } 708 return screenId; 709 } 710 handleActivityResult( final int requestCode, final int resultCode, final Intent data)711 private void handleActivityResult( 712 final int requestCode, final int resultCode, final Intent data) { 713 if (isWorkspaceLoading()) { 714 // process the result once the workspace has loaded. 715 mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data); 716 return; 717 } 718 mPendingActivityResult = null; 719 720 // Reset the startActivity waiting flag 721 final PendingRequestArgs requestArgs = mPendingRequestArgs; 722 setWaitingForResult(null); 723 if (requestArgs == null) { 724 return; 725 } 726 727 final int pendingAddWidgetId = requestArgs.getWidgetId(); 728 729 Runnable exitSpringLoaded = new Runnable() { 730 @Override 731 public void run() { 732 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 733 } 734 }; 735 736 if (requestCode == REQUEST_BIND_APPWIDGET) { 737 // This is called only if the user did not previously have permissions to bind widgets 738 final int appWidgetId = data != null ? 739 data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; 740 if (resultCode == RESULT_CANCELED) { 741 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs); 742 mWorkspace.removeExtraEmptyScreenDelayed( 743 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 744 } else if (resultCode == RESULT_OK) { 745 addAppWidgetImpl( 746 appWidgetId, requestArgs, null, 747 requestArgs.getWidgetHandler(), 748 ON_ACTIVITY_RESULT_ANIMATION_DELAY); 749 } 750 return; 751 } 752 753 boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || 754 requestCode == REQUEST_CREATE_APPWIDGET); 755 756 // We have special handling for widgets 757 if (isWidgetDrop) { 758 final int appWidgetId; 759 int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) 760 : -1; 761 if (widgetId < 0) { 762 appWidgetId = pendingAddWidgetId; 763 } else { 764 appWidgetId = widgetId; 765 } 766 767 final int result; 768 if (appWidgetId < 0 || resultCode == RESULT_CANCELED) { 769 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " + 770 "returned from the widget configuration activity."); 771 result = RESULT_CANCELED; 772 completeTwoStageWidgetDrop(result, appWidgetId, requestArgs); 773 mWorkspace.removeExtraEmptyScreenDelayed( 774 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, 775 () -> getStateManager().goToState(NORMAL)); 776 } else { 777 if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 778 // When the screen id represents an actual screen (as opposed to a rank) 779 // we make sure that the drop page actually exists. 780 requestArgs.screenId = 781 ensurePendingDropLayoutExists(requestArgs.screenId); 782 } 783 final CellLayout dropLayout = 784 mWorkspace.getScreenWithId(requestArgs.screenId); 785 786 dropLayout.setDropPending(true); 787 final Runnable onComplete = new Runnable() { 788 @Override 789 public void run() { 790 completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs); 791 dropLayout.setDropPending(false); 792 } 793 }; 794 mWorkspace.removeExtraEmptyScreenDelayed( 795 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, onComplete); 796 } 797 return; 798 } 799 800 if (requestCode == REQUEST_RECONFIGURE_APPWIDGET 801 || requestCode == REQUEST_BIND_PENDING_APPWIDGET) { 802 if (resultCode == RESULT_OK) { 803 // Update the widget view. 804 completeAdd(requestCode, data, pendingAddWidgetId, requestArgs); 805 } 806 // Leave the widget in the pending state if the user canceled the configure. 807 return; 808 } 809 810 if (requestCode == REQUEST_CREATE_SHORTCUT) { 811 // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT. 812 if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) { 813 completeAdd(requestCode, data, -1, requestArgs); 814 mWorkspace.removeExtraEmptyScreenDelayed( 815 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 816 817 } else if (resultCode == RESULT_CANCELED) { 818 mWorkspace.removeExtraEmptyScreenDelayed( 819 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 820 } 821 } 822 823 mDragLayer.clearAnimatedView(); 824 } 825 826 @Override onActivityResult( final int requestCode, final int resultCode, final Intent data)827 public void onActivityResult( 828 final int requestCode, final int resultCode, final Intent data) { 829 mPendingActivityRequestCode = -1; 830 handleActivityResult(requestCode, resultCode, data); 831 } 832 833 @Override onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)834 public void onRequestPermissionsResult(int requestCode, String[] permissions, 835 int[] grantResults) { 836 PendingRequestArgs pendingArgs = mPendingRequestArgs; 837 if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null 838 && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) { 839 setWaitingForResult(null); 840 841 View v = null; 842 CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId); 843 if (layout != null) { 844 v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY); 845 } 846 Intent intent = pendingArgs.getPendingIntent(); 847 848 if (grantResults.length > 0 849 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 850 startActivitySafely(v, intent, null); 851 } else { 852 // TODO: Show a snack bar with link to settings 853 Toast.makeText(this, getString(R.string.msg_no_phone_permission, 854 getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show(); 855 } 856 } 857 } 858 859 /** 860 * Check to see if a given screen id exists. If not, create it at the end, return the new id. 861 * 862 * @param screenId the screen id to check 863 * @return the new screen, or screenId if it exists 864 */ ensurePendingDropLayoutExists(int screenId)865 private int ensurePendingDropLayoutExists(int screenId) { 866 CellLayout dropLayout = mWorkspace.getScreenWithId(screenId); 867 if (dropLayout == null) { 868 // it's possible that the add screen was removed because it was 869 // empty and a re-bind occurred 870 mWorkspace.addExtraEmptyScreen(); 871 return mWorkspace.commitExtraEmptyScreen(); 872 } else { 873 return screenId; 874 } 875 } 876 877 @Thunk completeTwoStageWidgetDrop( final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs)878 void completeTwoStageWidgetDrop( 879 final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) { 880 CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId); 881 Runnable onCompleteRunnable = null; 882 int animationType = 0; 883 884 AppWidgetHostView boundWidget = null; 885 if (resultCode == RESULT_OK) { 886 animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; 887 final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId, 888 requestArgs.getWidgetHandler().getProviderInfo(this)); 889 boundWidget = layout; 890 onCompleteRunnable = new Runnable() { 891 @Override 892 public void run() { 893 completeAddAppWidget(appWidgetId, requestArgs, layout, null); 894 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 895 } 896 }; 897 } else if (resultCode == RESULT_CANCELED) { 898 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 899 animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; 900 } 901 if (mDragLayer.getAnimatedView() != null) { 902 mWorkspace.animateWidgetDrop(requestArgs, cellLayout, 903 (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, 904 animationType, boundWidget, true); 905 } else if (onCompleteRunnable != null) { 906 // The animated view may be null in the case of a rotation during widget configuration 907 onCompleteRunnable.run(); 908 } 909 } 910 911 @Override onStop()912 protected void onStop() { 913 super.onStop(); 914 if (mDeferOverlayCallbacks) { 915 checkIfOverlayStillDeferred(); 916 } else { 917 mOverlayManager.onActivityStopped(this); 918 } 919 hideKeyboard(); 920 logStopAndResume(false /* isResume */); 921 mAppWidgetHost.setActivityStarted(false); 922 NotificationListener.removeNotificationsChangedListener(); 923 } 924 925 @Override onStart()926 protected void onStart() { 927 Object traceToken = TraceHelper.INSTANCE.beginSection(ON_START_EVT, 928 TraceHelper.FLAG_UI_EVENT); 929 super.onStart(); 930 if (!mDeferOverlayCallbacks) { 931 mOverlayManager.onActivityStarted(this); 932 } 933 934 mAppWidgetHost.setActivityStarted(true); 935 TraceHelper.INSTANCE.endSection(traceToken); 936 } 937 938 @Override 939 @CallSuper onDeferredResumed()940 protected void onDeferredResumed() { 941 logStopAndResume(true /* isResume */); 942 943 // Process any items that were added while Launcher was away. 944 ItemInstallQueue.INSTANCE.get(this) 945 .resumeModelPush(FLAG_ACTIVITY_PAUSED); 946 947 // Refresh shortcuts if the permission changed. 948 mModel.validateModelDataOnResume(); 949 950 // Set the notification listener and fetch updated notifications when we resume 951 NotificationListener.setNotificationsChangedListener(mPopupDataProvider); 952 953 DiscoveryBounce.showForHomeIfNeeded(this); 954 mAppWidgetHost.setActivityResumed(true); 955 } 956 logStopAndResume(boolean isResume)957 private void logStopAndResume(boolean isResume) { 958 if (mPendingExecutor != null) return; 959 int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage(); 960 int statsLogOrdinal = mStateManager.getState().statsLogOrdinal; 961 962 StatsLogManager.EventEnum event; 963 StatsLogManager.StatsLogger logger = getStatsLogManager().logger(); 964 if (isResume) { 965 logger.withSrcState(LAUNCHER_STATE_BACKGROUND) 966 .withDstState(mStateManager.getState().statsLogOrdinal); 967 event = LAUNCHER_ONRESUME; 968 } else { /* command == Action.Command.STOP */ 969 logger.withSrcState(mStateManager.getState().statsLogOrdinal) 970 .withDstState(LAUNCHER_STATE_BACKGROUND); 971 event = LAUNCHER_ONSTOP; 972 } 973 974 if (statsLogOrdinal == LAUNCHER_STATE_HOME && mWorkspace != null) { 975 logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() 976 .setWorkspace( 977 LauncherAtom.WorkspaceContainer.newBuilder() 978 .setPageIndex(pageIndex)).build()); 979 } 980 logger.log(event); 981 } 982 scheduleDeferredCheck()983 private void scheduleDeferredCheck() { 984 mHandler.removeCallbacks(mDeferredOverlayCallbacks); 985 postAsyncCallback(mHandler, mDeferredOverlayCallbacks); 986 } 987 checkIfOverlayStillDeferred()988 private void checkIfOverlayStillDeferred() { 989 if (!mDeferOverlayCallbacks) { 990 return; 991 } 992 if (isStarted() && (!hasBeenResumed() 993 || mStateManager.getState().hasFlag(FLAG_NON_INTERACTIVE))) { 994 return; 995 } 996 mDeferOverlayCallbacks = false; 997 998 // Move the client to the correct state. Calling the same method twice is no-op. 999 if (isStarted()) { 1000 mOverlayManager.onActivityStarted(this); 1001 } 1002 if (hasBeenResumed()) { 1003 mOverlayManager.onActivityResumed(this); 1004 } else { 1005 mOverlayManager.onActivityPaused(this); 1006 } 1007 if (!isStarted()) { 1008 mOverlayManager.onActivityStopped(this); 1009 } 1010 } 1011 deferOverlayCallbacksUntilNextResumeOrStop()1012 public void deferOverlayCallbacksUntilNextResumeOrStop() { 1013 mDeferOverlayCallbacks = true; 1014 } 1015 getOverlayManager()1016 public LauncherOverlayManager getOverlayManager() { 1017 return mOverlayManager; 1018 } 1019 1020 @Override onStateSetStart(LauncherState state)1021 public void onStateSetStart(LauncherState state) { 1022 super.onStateSetStart(state); 1023 if (mDeferOverlayCallbacks) { 1024 scheduleDeferredCheck(); 1025 } 1026 addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE); 1027 1028 if (state.hasFlag(FLAG_CLOSE_POPUPS)) { 1029 AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE)); 1030 } 1031 1032 if (state == SPRING_LOADED) { 1033 // Prevent any Un/InstallShortcutReceivers from updating the db while we are 1034 // not on homescreen 1035 ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP); 1036 getRotationHelper().setCurrentStateRequest(REQUEST_LOCK); 1037 1038 mWorkspace.showPageIndicatorAtCurrentScroll(); 1039 mWorkspace.setClipChildren(false); 1040 } 1041 // When multiple pages are visible, show persistent page indicator 1042 mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE)); 1043 1044 mPrevLauncherState = mStateManager.getCurrentStableState(); 1045 if (mPrevLauncherState != state && ALL_APPS.equals(state) 1046 // Making sure mAllAppsSessionLogId is null to avoid double logging. 1047 && mAllAppsSessionLogId == null) { 1048 // creates new instance ID since new all apps session is started. 1049 mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId(); 1050 getStatsLogManager() 1051 .logger() 1052 .log(FeatureFlags.ENABLE_DEVICE_SEARCH.get() 1053 ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH 1054 : LAUNCHER_ALLAPPS_ENTRY); 1055 } 1056 } 1057 1058 @Override onStateSetEnd(LauncherState state)1059 public void onStateSetEnd(LauncherState state) { 1060 super.onStateSetEnd(state); 1061 getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL); 1062 getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE)); 1063 1064 finishAutoCancelActionMode(); 1065 removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE); 1066 1067 // dispatch window state changed 1068 getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED); 1069 AccessibilityManagerCompat.sendStateEventToTest(this, state.ordinal); 1070 1071 if (state == NORMAL) { 1072 // Re-enable any Un/InstallShortcutReceiver and now process any queued items 1073 ItemInstallQueue.INSTANCE.get(this) 1074 .resumeModelPush(FLAG_DRAG_AND_DROP); 1075 1076 // Clear any rotation locks when going to normal state 1077 getRotationHelper().setCurrentStateRequest(REQUEST_NONE); 1078 } 1079 1080 if (ALL_APPS.equals(mPrevLauncherState) && !ALL_APPS.equals(state) 1081 // Making sure mAllAppsSessionLogId is not null to avoid double logging. 1082 && mAllAppsSessionLogId != null) { 1083 getAppsView().reset(false); 1084 getStatsLogManager().logger() 1085 .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() 1086 .setWorkspace( 1087 LauncherAtom.WorkspaceContainer.newBuilder() 1088 .setPageIndex(getWorkspace().getCurrentPage())) 1089 .build()) 1090 .log(LAUNCHER_ALLAPPS_EXIT); 1091 mAllAppsSessionLogId = null; 1092 } 1093 } 1094 1095 @Override onResume()1096 protected void onResume() { 1097 Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT, 1098 TraceHelper.FLAG_UI_EVENT); 1099 super.onResume(); 1100 1101 if (mDeferOverlayCallbacks) { 1102 scheduleDeferredCheck(); 1103 } else { 1104 mOverlayManager.onActivityResumed(this); 1105 } 1106 1107 TraceHelper.INSTANCE.endSection(traceToken); 1108 } 1109 1110 @Override onPause()1111 protected void onPause() { 1112 // Ensure that items added to Launcher are queued until Launcher returns 1113 ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); 1114 1115 super.onPause(); 1116 mDragController.cancelDrag(); 1117 mLastTouchUpTime = -1; 1118 mDropTargetBar.animateToVisibility(false); 1119 1120 if (!mDeferOverlayCallbacks) { 1121 mOverlayManager.onActivityPaused(this); 1122 } 1123 mAppWidgetHost.setActivityResumed(false); 1124 } 1125 1126 class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks { 1127 onScrollChanged(float progress)1128 public void onScrollChanged(float progress) { 1129 if (mWorkspace != null) { 1130 mWorkspace.onOverlayScrollChanged(progress); 1131 } 1132 } 1133 } 1134 1135 /** 1136 * Restores the previous state, if it exists. 1137 * 1138 * @param savedState The previous state. 1139 */ restoreState(Bundle savedState)1140 private void restoreState(Bundle savedState) { 1141 if (savedState == null) { 1142 return; 1143 } 1144 1145 int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal); 1146 LauncherState[] stateValues = LauncherState.values(); 1147 LauncherState state = stateValues[stateOrdinal]; 1148 1149 NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance(); 1150 boolean forceRestore = lastInstance != null 1151 && (lastInstance.config.diff(mOldConfig) & CONFIG_UI_MODE) != 0; 1152 if (forceRestore || !state.shouldDisableRestore()) { 1153 mStateManager.goToState(state, false /* animated */); 1154 } 1155 1156 PendingRequestArgs requestArgs = savedState.getParcelable( 1157 RUNTIME_STATE_PENDING_REQUEST_ARGS); 1158 if (requestArgs != null) { 1159 setWaitingForResult(requestArgs); 1160 } 1161 mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE); 1162 1163 mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT); 1164 1165 SparseArray<Parcelable> widgetsState = 1166 savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL); 1167 if (widgetsState != null) { 1168 WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState); 1169 } 1170 } 1171 1172 /** 1173 * Finds all the views we need and configure them properly. 1174 */ setupViews()1175 protected void setupViews() { 1176 mDragLayer = findViewById(R.id.drag_layer); 1177 mFocusHandler = mDragLayer.getFocusIndicatorHelper(); 1178 mWorkspace = mDragLayer.findViewById(R.id.workspace); 1179 mWorkspace.initParentViews(mDragLayer); 1180 mOverviewPanel = findViewById(R.id.overview_panel); 1181 mHotseat = findViewById(R.id.hotseat); 1182 mHotseat.setWorkspace(mWorkspace); 1183 1184 // Setup the drag layer 1185 mDragLayer.setup(mDragController, mWorkspace); 1186 1187 mWorkspace.setup(mDragController); 1188 // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the 1189 // default state, otherwise we will update to the wrong offsets in RTL 1190 mWorkspace.lockWallpaperToDefaultPage(); 1191 mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */); 1192 mDragController.addDragListener(mWorkspace); 1193 1194 // Get the search/delete/uninstall bar 1195 mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar); 1196 1197 // Setup Apps 1198 mAppsView = findViewById(R.id.apps_view); 1199 1200 // Setup Scrim 1201 mScrimView = findViewById(R.id.scrim_view); 1202 1203 // Setup the drag controller (drop targets have to be added in reverse order in priority) 1204 mDropTargetBar.setup(mDragController); 1205 mAllAppsController.setupViews(mScrimView, mAppsView); 1206 } 1207 1208 /** 1209 * Creates a view representing a shortcut. 1210 * 1211 * @param info The data structure describing the shortcut. 1212 */ createShortcut(WorkspaceItemInfo info)1213 View createShortcut(WorkspaceItemInfo info) { 1214 return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); 1215 } 1216 1217 /** 1218 * Creates a view representing a shortcut inflated from the specified resource. 1219 * 1220 * @param parent The group the shortcut belongs to. 1221 * @param info The data structure describing the shortcut. 1222 * @return A View inflated from layoutResId. 1223 */ createShortcut(ViewGroup parent, WorkspaceItemInfo info)1224 public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) { 1225 BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext()) 1226 .inflate(R.layout.app_icon, parent, false); 1227 favorite.applyFromWorkspaceItem(info); 1228 favorite.setOnClickListener(ItemClickHandler.INSTANCE); 1229 favorite.setOnFocusChangeListener(mFocusHandler); 1230 return favorite; 1231 } 1232 1233 /** 1234 * Add a shortcut to the workspace or to a Folder. 1235 * 1236 * @param data The intent describing the shortcut. 1237 */ completeAddShortcut(Intent data, int container, int screenId, int cellX, int cellY, PendingRequestArgs args)1238 private void completeAddShortcut(Intent data, int container, int screenId, int cellX, 1239 int cellY, PendingRequestArgs args) { 1240 if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT 1241 || args.getPendingIntent().getComponent() == null) { 1242 return; 1243 } 1244 1245 int[] cellXY = mTmpAddItemCellCoordinates; 1246 CellLayout layout = getCellLayout(container, screenId); 1247 1248 WorkspaceItemInfo info = PinRequestHelper.createWorkspaceItemFromPinItemRequest( 1249 this, PinRequestHelper.getPinItemRequest(data), 0); 1250 1251 if (info == null) { 1252 // Legacy shortcuts are only supported for primary profile. 1253 info = Process.myUserHandle().equals(args.user) 1254 ? ModelUtils.fromLegacyShortcutIntent(this, data) : null; 1255 1256 if (info == null) { 1257 Log.e(TAG, "Unable to parse a valid custom shortcut result"); 1258 return; 1259 } else if (!new PackageManagerHelper(this).hasPermissionForActivity( 1260 info.intent, args.getPendingIntent().getComponent().getPackageName())) { 1261 // The app is trying to add a shortcut without sufficient permissions 1262 Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0)); 1263 return; 1264 } 1265 } 1266 1267 if (container < 0) { 1268 // Adding a shortcut to the Workspace. 1269 final View view = createShortcut(info); 1270 boolean foundCellSpan = false; 1271 // First we check if we already know the exact location where we want to add this item. 1272 if (cellX >= 0 && cellY >= 0) { 1273 cellXY[0] = cellX; 1274 cellXY[1] = cellY; 1275 foundCellSpan = true; 1276 1277 DragObject dragObject = new DragObject(getApplicationContext()); 1278 dragObject.dragInfo = info; 1279 // If appropriate, either create a folder or add to an existing folder 1280 if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0, 1281 true, dragObject)) { 1282 return; 1283 } 1284 if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject, 1285 true)) { 1286 return; 1287 } 1288 } else { 1289 foundCellSpan = layout.findCellForSpan(cellXY, 1, 1); 1290 } 1291 1292 if (!foundCellSpan) { 1293 mWorkspace.onNoCellFound(layout); 1294 return; 1295 } 1296 1297 getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]); 1298 mWorkspace.addInScreen(view, info); 1299 } else { 1300 // Adding a shortcut to a Folder. 1301 FolderIcon folderIcon = findFolderIcon(container); 1302 if (folderIcon != null) { 1303 FolderInfo folderInfo = (FolderInfo) folderIcon.getTag(); 1304 folderInfo.add(info, args.rank, false); 1305 } else { 1306 Log.e(TAG, "Could not find folder with id " + container + " to add shortcut."); 1307 } 1308 } 1309 } 1310 findFolderIcon(final int folderIconId)1311 public FolderIcon findFolderIcon(final int folderIconId) { 1312 return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId); 1313 } 1314 1315 /** 1316 * Add a widget to the workspace. 1317 * 1318 * @param appWidgetId The app widget id 1319 */ 1320 @Thunk completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo)1321 void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, 1322 AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { 1323 1324 if (appWidgetInfo == null) { 1325 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId); 1326 } 1327 1328 if (hostView == null) { 1329 // Perform actual inflation because we're live 1330 hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 1331 } 1332 1333 LauncherAppWidgetInfo launcherInfo; 1334 launcherInfo = 1335 new LauncherAppWidgetInfo( 1336 appWidgetId, appWidgetInfo.provider, appWidgetInfo, hostView); 1337 launcherInfo.spanX = itemInfo.spanX; 1338 launcherInfo.spanY = itemInfo.spanY; 1339 launcherInfo.minSpanX = itemInfo.minSpanX; 1340 launcherInfo.minSpanY = itemInfo.minSpanY; 1341 launcherInfo.user = appWidgetInfo.getProfile(); 1342 if (itemInfo instanceof PendingAddWidgetInfo) { 1343 launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer; 1344 } else if (itemInfo instanceof PendingRequestArgs) { 1345 launcherInfo.sourceContainer = 1346 ((PendingRequestArgs) itemInfo).getWidgetSourceContainer(); 1347 } 1348 1349 getModelWriter().addItemToDatabase(launcherInfo, 1350 itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); 1351 1352 hostView.setVisibility(View.VISIBLE); 1353 prepareAppWidget(hostView, launcherInfo); 1354 mWorkspace.addInScreen(hostView, launcherInfo); 1355 announceForAccessibility(R.string.item_added_to_workspace); 1356 1357 // Show the widget resize frame. 1358 if (hostView instanceof LauncherAppWidgetHostView) { 1359 final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView; 1360 CellLayout cellLayout = getCellLayout(launcherInfo.container, launcherInfo.screenId); 1361 if (mStateManager.getState() == NORMAL) { 1362 // Show resize frame once the widget layout is drawn. 1363 View.OnLayoutChangeListener onLayoutChangeListener = 1364 new View.OnLayoutChangeListener() { 1365 @Override 1366 public void onLayoutChange(View view, int left, int top, int right, 1367 int bottom, int oldLeft, int oldTop, int oldRight, 1368 int oldBottom) { 1369 AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout); 1370 launcherHostView.removeOnLayoutChangeListener(this); 1371 } 1372 }; 1373 launcherHostView.addOnLayoutChangeListener(onLayoutChangeListener); 1374 // There is a small chance that the layout was already drawn before the layout 1375 // change listener was registered, which means that the resize frame wouldn't be 1376 // shown. Directly call requestLayout to force a layout change. 1377 launcherHostView.requestLayout(); 1378 } else { 1379 mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() { 1380 @Override 1381 public void onStateTransitionComplete(LauncherState finalState) { 1382 if (mPrevLauncherState == SPRING_LOADED && finalState == NORMAL) { 1383 AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout); 1384 mStateManager.removeStateListener(this); 1385 } 1386 } 1387 }); 1388 } 1389 } 1390 } 1391 prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item)1392 private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) { 1393 hostView.setTag(item); 1394 item.onBindAppWidget(this, hostView); 1395 hostView.setFocusable(true); 1396 hostView.setOnFocusChangeListener(mFocusHandler); 1397 } 1398 1399 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 1400 @Override 1401 public void onReceive(Context context, Intent intent) { 1402 onScreenOff(); 1403 } 1404 }; 1405 updateNotificationDots(Predicate<PackageUserKey> updatedDots)1406 private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 1407 mWorkspace.updateNotificationDots(updatedDots); 1408 mAppsView.getAppsStore().updateNotificationDots(updatedDots); 1409 } 1410 1411 @Override onAttachedToWindow()1412 public void onAttachedToWindow() { 1413 super.onAttachedToWindow(); 1414 mOverlayManager.onAttachedToWindow(); 1415 } 1416 1417 @Override onDetachedFromWindow()1418 public void onDetachedFromWindow() { 1419 super.onDetachedFromWindow(); 1420 mOverlayManager.onDetachedFromWindow(); 1421 closeContextMenu(); 1422 } 1423 1424 @Override onRetainNonConfigurationInstance()1425 public Object onRetainNonConfigurationInstance() { 1426 NonConfigInstance instance = new NonConfigInstance(); 1427 instance.config = new Configuration(mOldConfig); 1428 1429 int width = mDragLayer.getWidth(); 1430 int height = mDragLayer.getHeight(); 1431 1432 if (FeatureFlags.ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE.get() 1433 && width > 0 1434 && height > 0) { 1435 instance.snapshot = 1436 BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw); 1437 } 1438 return instance; 1439 } 1440 getAllAppsController()1441 public AllAppsTransitionController getAllAppsController() { 1442 return mAllAppsController; 1443 } 1444 1445 @Override getDragLayer()1446 public DragLayer getDragLayer() { 1447 return mDragLayer; 1448 } 1449 getAppsView()1450 public AllAppsContainerView getAppsView() { 1451 return mAppsView; 1452 } 1453 getWorkspace()1454 public Workspace getWorkspace() { 1455 return mWorkspace; 1456 } 1457 getHotseat()1458 public Hotseat getHotseat() { 1459 return mHotseat; 1460 } 1461 getOverviewPanel()1462 public <T extends View> T getOverviewPanel() { 1463 return (T) mOverviewPanel; 1464 } 1465 getDropTargetBar()1466 public DropTargetBar getDropTargetBar() { 1467 return mDropTargetBar; 1468 } 1469 1470 @Override getScrimView()1471 public ScrimView getScrimView() { 1472 return mScrimView; 1473 } 1474 getAppWidgetHost()1475 public LauncherAppWidgetHost getAppWidgetHost() { 1476 return mAppWidgetHost; 1477 } 1478 createAppWidgetHost()1479 protected LauncherAppWidgetHost createAppWidgetHost() { 1480 return new LauncherAppWidgetHost(this, 1481 appWidgetId -> getWorkspace().removeWidget(appWidgetId)); 1482 } 1483 getModel()1484 public LauncherModel getModel() { 1485 return mModel; 1486 } 1487 getModelWriter()1488 public ModelWriter getModelWriter() { 1489 return mModelWriter; 1490 } 1491 1492 @Override getSharedPrefs()1493 public SharedPreferences getSharedPrefs() { 1494 return mSharedPrefs; 1495 } 1496 1497 @Override getDevicePrefs()1498 public SharedPreferences getDevicePrefs() { 1499 return Utilities.getDevicePrefs(this); 1500 } 1501 getOrientation()1502 public int getOrientation() { 1503 return mOldConfig.orientation; 1504 } 1505 1506 @Override onNewIntent(Intent intent)1507 protected void onNewIntent(Intent intent) { 1508 if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { 1509 Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent); 1510 } 1511 Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT); 1512 super.onNewIntent(intent); 1513 1514 boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() & 1515 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) 1516 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 1517 1518 // Check this condition before handling isActionMain, as this will get reset. 1519 boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL) 1520 && AbstractFloatingView.getTopOpenView(this) == null; 1521 boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); 1522 boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this); 1523 hideKeyboard(); 1524 if (isActionMain) { 1525 if (!internalStateHandled) { 1526 // In all these cases, only animate if we're already on home 1527 closeOpenViews(isStarted()); 1528 1529 if (!isInState(NORMAL)) { 1530 // Only change state, if not already the same. This prevents cancelling any 1531 // animations running as part of resume 1532 mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange()); 1533 } 1534 1535 // Reset the apps view 1536 if (!alreadyOnHome) { 1537 mAppsView.reset(isStarted() /* animate */); 1538 } 1539 1540 if (shouldMoveToDefaultScreen && !mWorkspace.isHandlingTouch()) { 1541 mWorkspace.post(mWorkspace::moveToDefaultScreen); 1542 } 1543 } 1544 1545 if (mLauncherCallbacks != null) { 1546 mLauncherCallbacks.onHomeIntent(internalStateHandled); 1547 } 1548 mOverlayManager.hideOverlay(isStarted() && !isForceInvisible()); 1549 handleGestureContract(intent); 1550 } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) { 1551 showAllAppsFromIntent(alreadyOnHome); 1552 } 1553 1554 TraceHelper.INSTANCE.endSection(traceToken); 1555 } 1556 showAllAppsFromIntent(boolean alreadyOnHome)1557 protected void showAllAppsFromIntent(boolean alreadyOnHome) { 1558 AbstractFloatingView.closeAllOpenViews(this); 1559 getStateManager().goToState(ALL_APPS, alreadyOnHome); 1560 } 1561 1562 /** 1563 * Handles gesture nav contract 1564 */ handleGestureContract(Intent intent)1565 protected void handleGestureContract(Intent intent) { 1566 GestureNavContract gnc = GestureNavContract.fromIntent(intent); 1567 if (gnc != null) { 1568 AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE); 1569 FloatingSurfaceView.show(this, gnc); 1570 } 1571 } 1572 1573 /** 1574 * Hides the keyboard if visible 1575 */ hideKeyboard()1576 public void hideKeyboard() { 1577 final View v = getWindow().peekDecorView(); 1578 if (v != null && v.getWindowToken() != null) { 1579 UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken()); 1580 } 1581 } 1582 1583 @Override onRestoreInstanceState(Bundle state)1584 public void onRestoreInstanceState(Bundle state) { 1585 super.onRestoreInstanceState(state); 1586 mWorkspace.restoreInstanceStateForChild(mSynchronouslyBoundPage); 1587 } 1588 1589 @Override onSaveInstanceState(Bundle outState)1590 protected void onSaveInstanceState(Bundle outState) { 1591 if (mWorkspace.getChildCount() > 0) { 1592 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); 1593 1594 } 1595 outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal); 1596 1597 1598 AbstractFloatingView widgets = AbstractFloatingView 1599 .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET); 1600 if (widgets != null) { 1601 SparseArray<Parcelable> widgetsState = new SparseArray<>(); 1602 widgets.saveHierarchyState(widgetsState); 1603 outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState); 1604 } else { 1605 outState.remove(RUNTIME_STATE_WIDGET_PANEL); 1606 } 1607 1608 // We close any open folders and shortcut containers that are not safe for rebind, 1609 // and we need to make sure this state is reflected. 1610 AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE); 1611 finishAutoCancelActionMode(); 1612 1613 if (mPendingRequestArgs != null) { 1614 outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); 1615 } 1616 outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode); 1617 1618 if (mPendingActivityResult != null) { 1619 outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult); 1620 } 1621 1622 super.onSaveInstanceState(outState); 1623 mOverlayManager.onActivitySaveInstanceState(this, outState); 1624 } 1625 1626 @Override onDestroy()1627 public void onDestroy() { 1628 super.onDestroy(); 1629 ACTIVITY_TRACKER.onActivityDestroyed(this); 1630 1631 unregisterReceiver(mScreenOffReceiver); 1632 mWorkspace.removeFolderListeners(); 1633 PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this); 1634 1635 mModel.removeCallbacks(this); 1636 mRotationHelper.destroy(); 1637 1638 try { 1639 mAppWidgetHost.stopListening(); 1640 } catch (NullPointerException ex) { 1641 Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); 1642 } 1643 1644 TextKeyListener.getInstance().release(); 1645 clearPendingBinds(); 1646 LauncherAppState.getIDP(this).removeOnChangeListener(this); 1647 1648 mOverlayManager.onActivityDestroyed(this); 1649 mUserChangedCallbackCloseable.close(); 1650 } 1651 getAccessibilityDelegate()1652 public LauncherAccessibilityDelegate getAccessibilityDelegate() { 1653 return mAccessibilityDelegate; 1654 } 1655 getDragController()1656 public DragController getDragController() { 1657 return mDragController; 1658 } 1659 1660 @Override startActivityForResult(Intent intent, int requestCode, Bundle options)1661 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 1662 if (requestCode != -1) { 1663 mPendingActivityRequestCode = requestCode; 1664 } 1665 super.startActivityForResult(intent, requestCode, options); 1666 } 1667 1668 @Override startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)1669 public void startIntentSenderForResult(IntentSender intent, int requestCode, 1670 Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { 1671 if (requestCode != -1) { 1672 mPendingActivityRequestCode = requestCode; 1673 } 1674 try { 1675 super.startIntentSenderForResult(intent, requestCode, 1676 fillInIntent, flagsMask, flagsValues, extraFlags, options); 1677 } catch (IntentSender.SendIntentException e) { 1678 throw new ActivityNotFoundException(); 1679 } 1680 } 1681 1682 /** 1683 * Indicates that we want global search for this activity by setting the globalSearch 1684 * argument for {@link #startSearch} to true. 1685 */ 1686 @Override startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)1687 public void startSearch(String initialQuery, boolean selectInitialQuery, 1688 Bundle appSearchData, boolean globalSearch) { 1689 if (appSearchData == null) { 1690 appSearchData = new Bundle(); 1691 appSearchData.putString("source", "launcher-search"); 1692 } 1693 1694 if (mLauncherCallbacks == null || 1695 !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) { 1696 // Starting search from the callbacks failed. Start the default global search. 1697 super.startSearch(initialQuery, selectInitialQuery, appSearchData, true); 1698 } 1699 1700 // We need to show the workspace after starting the search 1701 mStateManager.goToState(NORMAL); 1702 } 1703 isWorkspaceLocked()1704 public boolean isWorkspaceLocked() { 1705 return mWorkspaceLoading || mPendingRequestArgs != null; 1706 } 1707 isWorkspaceLoading()1708 public boolean isWorkspaceLoading() { 1709 return mWorkspaceLoading; 1710 } 1711 setWorkspaceLoading(boolean value)1712 private void setWorkspaceLoading(boolean value) { 1713 mWorkspaceLoading = value; 1714 } 1715 setWaitingForResult(PendingRequestArgs args)1716 public void setWaitingForResult(PendingRequestArgs args) { 1717 mPendingRequestArgs = args; 1718 } 1719 addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler)1720 void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, 1721 WidgetAddFlowHandler addFlowHandler) { 1722 if (LOGD) { 1723 Log.d(TAG, "Adding widget from drop"); 1724 } 1725 addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0); 1726 } 1727 addAppWidgetImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay)1728 void addAppWidgetImpl(int appWidgetId, ItemInfo info, 1729 AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) { 1730 if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, 1731 REQUEST_CREATE_APPWIDGET)) { 1732 // If the configuration flow was not started, add the widget 1733 1734 Runnable onComplete = new Runnable() { 1735 @Override 1736 public void run() { 1737 // Exit spring loaded mode if necessary after adding the widget 1738 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 1739 } 1740 }; 1741 completeAddAppWidget(appWidgetId, info, boundWidget, 1742 addFlowHandler.getProviderInfo(this)); 1743 mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete); 1744 } 1745 } 1746 addPendingItem(PendingAddItemInfo info, int container, int screenId, int[] cell, int spanX, int spanY)1747 public void addPendingItem(PendingAddItemInfo info, int container, int screenId, 1748 int[] cell, int spanX, int spanY) { 1749 info.container = container; 1750 info.screenId = screenId; 1751 if (cell != null) { 1752 info.cellX = cell[0]; 1753 info.cellY = cell[1]; 1754 } 1755 info.spanX = spanX; 1756 info.spanY = spanY; 1757 1758 switch (info.itemType) { 1759 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: 1760 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1761 addAppWidgetFromDrop((PendingAddWidgetInfo) info); 1762 break; 1763 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1764 processShortcutFromDrop((PendingAddShortcutInfo) info); 1765 break; 1766 default: 1767 throw new IllegalStateException("Unknown item type: " + info.itemType); 1768 } 1769 } 1770 1771 /** 1772 * Process a shortcut drop. 1773 */ processShortcutFromDrop(PendingAddShortcutInfo info)1774 private void processShortcutFromDrop(PendingAddShortcutInfo info) { 1775 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName); 1776 setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info)); 1777 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: processShortcutFromDrop"); 1778 if (!info.activityInfo.startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) { 1779 handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null); 1780 } 1781 } 1782 1783 /** 1784 * Process a widget drop. 1785 */ addAppWidgetFromDrop(PendingAddWidgetInfo info)1786 private void addAppWidgetFromDrop(PendingAddWidgetInfo info) { 1787 AppWidgetHostView hostView = info.boundWidget; 1788 final int appWidgetId; 1789 WidgetAddFlowHandler addFlowHandler = info.getHandler(); 1790 if (hostView != null) { 1791 // In the case where we've prebound the widget, we remove it from the DragLayer 1792 if (LOGD) { 1793 Log.d(TAG, "Removing widget view from drag layer and setting boundWidget to null"); 1794 } 1795 getDragLayer().removeView(hostView); 1796 1797 appWidgetId = hostView.getAppWidgetId(); 1798 addAppWidgetFromDropImpl(appWidgetId, info, hostView, addFlowHandler); 1799 1800 // Clear the boundWidget so that it doesn't get destroyed. 1801 info.boundWidget = null; 1802 } else { 1803 // In this case, we either need to start an activity to get permission to bind 1804 // the widget, or we need to start an activity to configure the widget, or both. 1805 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { 1806 appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider( 1807 info.componentName); 1808 } else { 1809 appWidgetId = getAppWidgetHost().allocateAppWidgetId(); 1810 } 1811 Bundle options = info.bindOptions; 1812 1813 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 1814 appWidgetId, info.info, options); 1815 if (success) { 1816 addAppWidgetFromDropImpl(appWidgetId, info, null, addFlowHandler); 1817 } else { 1818 addFlowHandler.startBindFlow(this, appWidgetId, info, REQUEST_BIND_APPWIDGET); 1819 } 1820 } 1821 } 1822 1823 /** 1824 * Creates and adds new folder to CellLayout 1825 */ addFolder(CellLayout layout, int container, final int screenId, int cellX, int cellY)1826 public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, 1827 int cellY) { 1828 final FolderInfo folderInfo = new FolderInfo(); 1829 1830 // Update the model 1831 getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); 1832 1833 // Create the view 1834 FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout, 1835 folderInfo); 1836 mWorkspace.addInScreen(newFolder, folderInfo); 1837 // Force measure the new folder icon 1838 CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder); 1839 parent.getShortcutsAndWidgets().measureChild(newFolder); 1840 return newFolder; 1841 } 1842 1843 @Override getFolderBoundingBox()1844 public Rect getFolderBoundingBox() { 1845 // We need to bound the folder to the currently visible workspace area 1846 return getWorkspace().getPageAreaRelativeToDragLayer(); 1847 } 1848 1849 @Override updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height)1850 public void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) { 1851 int left = inOutPosition[0]; 1852 int top = inOutPosition[1]; 1853 DeviceProfile grid = getDeviceProfile(); 1854 int distFromEdgeOfScreen = getWorkspace().getPaddingLeft(); 1855 if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) { 1856 // Center the folder if it is very close to being centered anyway, by virtue of 1857 // filling the majority of the viewport. ie. remove it from the uncanny valley 1858 // of centeredness. 1859 left = (grid.availableWidthPx - width) / 2; 1860 } else if (width >= bounds.width()) { 1861 // If the folder doesn't fit within the bounds, center it about the desired bounds 1862 left = bounds.left + (bounds.width() - width) / 2; 1863 } 1864 if (height >= bounds.height()) { 1865 // Folder height is greater than page height, center on page 1866 top = bounds.top + (bounds.height() - height) / 2; 1867 } else { 1868 // Folder height is less than page height, so bound it to the absolute open folder 1869 // bounds if necessary 1870 Rect folderBounds = grid.getAbsoluteOpenFolderBounds(); 1871 left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width)); 1872 top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height)); 1873 } 1874 inOutPosition[0] = left; 1875 inOutPosition[1] = top; 1876 } 1877 1878 /** 1879 * Unbinds the view for the specified item, and removes the item and all its children. 1880 * 1881 * @param v the view being removed. 1882 * @param itemInfo the {@link ItemInfo} for this view. 1883 * @param deleteFromDb whether or not to delete this item from the db. 1884 */ removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb)1885 public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) { 1886 if (itemInfo instanceof WorkspaceItemInfo) { 1887 // Remove the shortcut from the folder before removing it from launcher 1888 View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container); 1889 if (folderIcon instanceof FolderIcon) { 1890 ((FolderInfo) folderIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true); 1891 } else { 1892 mWorkspace.removeWorkspaceItem(v); 1893 } 1894 if (deleteFromDb) { 1895 getModelWriter().deleteItemFromDatabase(itemInfo); 1896 } 1897 } else if (itemInfo instanceof FolderInfo) { 1898 final FolderInfo folderInfo = (FolderInfo) itemInfo; 1899 if (v instanceof FolderIcon) { 1900 ((FolderIcon) v).removeListeners(); 1901 } 1902 mWorkspace.removeWorkspaceItem(v); 1903 if (deleteFromDb) { 1904 getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo); 1905 } 1906 } else if (itemInfo instanceof LauncherAppWidgetInfo) { 1907 final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; 1908 mWorkspace.removeWorkspaceItem(v); 1909 if (deleteFromDb) { 1910 getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost()); 1911 } 1912 } else { 1913 return false; 1914 } 1915 return true; 1916 } 1917 1918 @Override dispatchKeyEvent(KeyEvent event)1919 public boolean dispatchKeyEvent(KeyEvent event) { 1920 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event); 1921 return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event); 1922 } 1923 1924 @Override dispatchTouchEvent(MotionEvent ev)1925 public boolean dispatchTouchEvent(MotionEvent ev) { 1926 switch (ev.getAction()) { 1927 case MotionEvent.ACTION_DOWN: 1928 mTouchInProgress = true; 1929 break; 1930 case MotionEvent.ACTION_UP: 1931 mLastTouchUpTime = SystemClock.uptimeMillis(); 1932 // Follow through 1933 case MotionEvent.ACTION_CANCEL: 1934 mTouchInProgress = false; 1935 break; 1936 } 1937 TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); 1938 return super.dispatchTouchEvent(ev); 1939 } 1940 1941 /** 1942 * Returns true if a touch interaction is in progress 1943 */ isTouchInProgress()1944 public boolean isTouchInProgress() { 1945 return mTouchInProgress; 1946 } 1947 1948 @Override onBackPressed()1949 public void onBackPressed() { 1950 if (finishAutoCancelActionMode()) { 1951 return; 1952 } 1953 1954 if (mDragController.isDragging()) { 1955 mDragController.cancelDrag(); 1956 return; 1957 } 1958 1959 // Note: There should be at most one log per method call. This is enforced implicitly 1960 // by using if-else statements. 1961 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); 1962 if (topView != null && topView.onBackPressed()) { 1963 // Handled by the floating view. 1964 } else { 1965 mStateManager.getState().onBackPressed(this); 1966 } 1967 } 1968 onScreenOff()1969 protected void onScreenOff() { 1970 // Reset AllApps to its initial state only if we are not in the middle of 1971 // processing a multi-step drop 1972 if (mPendingRequestArgs == null) { 1973 if (!isInState(NORMAL)) { 1974 onUiChangedWhileSleeping(); 1975 } 1976 mStateManager.goToState(NORMAL); 1977 } 1978 } 1979 1980 @TargetApi(Build.VERSION_CODES.M) 1981 @Override onErrorStartingShortcut(Intent intent, ItemInfo info)1982 protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { 1983 // Due to legacy reasons, direct call shortcuts require Launchers to have the 1984 // corresponding permission. Show the appropriate permission prompt if that 1985 // is the case. 1986 if (intent.getComponent() == null 1987 && Intent.ACTION_CALL.equals(intent.getAction()) 1988 && checkSelfPermission(android.Manifest.permission.CALL_PHONE) != 1989 PackageManager.PERMISSION_GRANTED) { 1990 1991 setWaitingForResult(PendingRequestArgs 1992 .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); 1993 requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE}, 1994 REQUEST_PERMISSION_CALL_PHONE); 1995 return true; 1996 } else { 1997 return false; 1998 } 1999 } 2000 2001 @Override startActivitySafely(View v, Intent intent, ItemInfo item)2002 public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { 2003 if (!hasBeenResumed()) { 2004 // Workaround an issue where the WM launch animation is clobbered when finishing the 2005 // recents animation into launcher. Defer launching the activity until Launcher is 2006 // next resumed. 2007 addOnResumeCallback(() -> startActivitySafely(v, intent, item)); 2008 if (mOnDeferredActivityLaunchCallback != null) { 2009 mOnDeferredActivityLaunchCallback.run(); 2010 mOnDeferredActivityLaunchCallback = null; 2011 } 2012 return true; 2013 } 2014 2015 boolean success = super.startActivitySafely(v, intent, item); 2016 if (success && v instanceof BubbleTextView) { 2017 // This is set to the view that launched the activity that navigated the user away 2018 // from launcher. Since there is no callback for when the activity has finished 2019 // launching, enable the press state and keep this reference to reset the press 2020 // state when we return to launcher. 2021 BubbleTextView btv = (BubbleTextView) v; 2022 btv.setStayPressed(true); 2023 addOnResumeCallback(() -> btv.setStayPressed(false)); 2024 } 2025 return success; 2026 } 2027 isHotseatLayout(View layout)2028 boolean isHotseatLayout(View layout) { 2029 // TODO: Remove this method 2030 return mHotseat != null && (layout == mHotseat); 2031 } 2032 2033 /** 2034 * Returns the CellLayout of the specified container at the specified screen. 2035 */ getCellLayout(int container, int screenId)2036 public CellLayout getCellLayout(int container, int screenId) { 2037 return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) 2038 ? mHotseat : mWorkspace.getScreenWithId(screenId); 2039 } 2040 2041 @Override onTrimMemory(int level)2042 public void onTrimMemory(int level) { 2043 super.onTrimMemory(level); 2044 if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 2045 // The widget preview db can result in holding onto over 2046 // 3MB of memory for caching which isn't necessary. 2047 SQLiteDatabase.releaseMemory(); 2048 2049 // This clears all widget bitmaps from the widget tray 2050 // TODO(hyunyoungs) 2051 } 2052 } 2053 2054 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)2055 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 2056 final boolean result = super.dispatchPopulateAccessibilityEvent(event); 2057 final List<CharSequence> text = event.getText(); 2058 text.clear(); 2059 // Populate event with a fake title based on the current state. 2060 // TODO: When can workspace be null? 2061 text.add(mWorkspace == null 2062 ? getString(R.string.home_screen) 2063 : mStateManager.getState().getDescription(this)); 2064 return result; 2065 } 2066 2067 /** 2068 * Persistant callback which notifies when an activity launch is deferred because the activity 2069 * was not yet resumed. 2070 */ setOnDeferredActivityLaunchCallback(Runnable callback)2071 public void setOnDeferredActivityLaunchCallback(Runnable callback) { 2072 mOnDeferredActivityLaunchCallback = callback; 2073 } 2074 2075 /** 2076 * Sets the next page to bind synchronously on next bind. 2077 * @param page 2078 */ setPageToBindSynchronously(int page)2079 public void setPageToBindSynchronously(int page) { 2080 mPageToBindSynchronously = page; 2081 } 2082 2083 /** 2084 * Implementation of the method from LauncherModel.Callbacks. 2085 */ 2086 @Override getPageToBindSynchronously()2087 public int getPageToBindSynchronously() { 2088 if (mPageToBindSynchronously != PagedView.INVALID_PAGE) { 2089 return mPageToBindSynchronously; 2090 } else if (mWorkspace != null) { 2091 return mWorkspace.getCurrentPage(); 2092 } else { 2093 return 0; 2094 } 2095 } 2096 2097 /** 2098 * Clear any pending bind callbacks. This is called when is loader is planning to 2099 * perform a full rebind from scratch. 2100 */ 2101 @Override clearPendingBinds()2102 public void clearPendingBinds() { 2103 if (mPendingExecutor != null) { 2104 mPendingExecutor.markCompleted(); 2105 mPendingExecutor = null; 2106 2107 // We might have set this flag previously and forgot to clear it. 2108 mAppsView.getAppsStore() 2109 .disableDeferUpdatesSilently(AllAppsStore.DEFER_UPDATES_NEXT_DRAW); 2110 } 2111 } 2112 2113 /** 2114 * Refreshes the shortcuts shown on the workspace. 2115 * 2116 * Implementation of the method from LauncherModel.Callbacks. 2117 */ startBinding()2118 public void startBinding() { 2119 Object traceToken = TraceHelper.INSTANCE.beginSection("startBinding"); 2120 // Floating panels (except the full widget sheet) are associated with individual icons. If 2121 // we are starting a fresh bind, close all such panels as all the icons are about 2122 // to go away. 2123 AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE); 2124 2125 setWorkspaceLoading(true); 2126 2127 // Clear the workspace because it's going to be rebound 2128 mDragController.cancelDrag(); 2129 2130 mWorkspace.clearDropTargets(); 2131 mWorkspace.removeAllWorkspaceScreens(); 2132 mAppWidgetHost.clearViews(); 2133 2134 if (mHotseat != null) { 2135 mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); 2136 } 2137 TraceHelper.INSTANCE.endSection(traceToken); 2138 } 2139 2140 @Override bindScreens(IntArray orderedScreenIds)2141 public void bindScreens(IntArray orderedScreenIds) { 2142 // Make sure the first screen is always at the start. 2143 if (FeatureFlags.QSB_ON_FIRST_SCREEN && 2144 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) { 2145 orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID); 2146 orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID); 2147 } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { 2148 // If there are no screens, we need to have an empty screen 2149 mWorkspace.addExtraEmptyScreen(); 2150 } 2151 bindAddScreens(orderedScreenIds); 2152 2153 // After we have added all the screens, if the wallpaper was locked to the default state, 2154 // then notify to indicate that it can be released and a proper wallpaper offset can be 2155 // computed before the next layout 2156 mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout(); 2157 } 2158 bindAddScreens(IntArray orderedScreenIds)2159 private void bindAddScreens(IntArray orderedScreenIds) { 2160 int count = orderedScreenIds.size(); 2161 for (int i = 0; i < count; i++) { 2162 int screenId = orderedScreenIds.get(i); 2163 if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) { 2164 // No need to bind the first screen, as its always bound. 2165 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId); 2166 } 2167 } 2168 } 2169 2170 @Override preAddApps()2171 public void preAddApps() { 2172 // If there's an undo snackbar, force it to complete to ensure empty screens are removed 2173 // before trying to add new items. 2174 mModelWriter.commitDelete(); 2175 AbstractFloatingView snackbar = AbstractFloatingView.getOpenView(this, TYPE_SNACKBAR); 2176 if (snackbar != null) { 2177 snackbar.post(() -> snackbar.close(true)); 2178 } 2179 } 2180 2181 @Override bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)2182 public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, 2183 ArrayList<ItemInfo> addAnimated) { 2184 // Add the new screens 2185 if (newScreens != null) { 2186 bindAddScreens(newScreens); 2187 } 2188 2189 // We add the items without animation on non-visible pages, and with 2190 // animations on the new page (which we will try and snap to). 2191 if (addNotAnimated != null && !addNotAnimated.isEmpty()) { 2192 bindItems(addNotAnimated, false); 2193 } 2194 if (addAnimated != null && !addAnimated.isEmpty()) { 2195 bindItems(addAnimated, true); 2196 } 2197 2198 // Remove the extra empty screen 2199 mWorkspace.removeExtraEmptyScreen(false); 2200 } 2201 2202 /** 2203 * Bind the items start-end from the list. 2204 * 2205 * Implementation of the method from LauncherModel.Callbacks. 2206 */ 2207 @Override bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons)2208 public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) { 2209 bindItems(items, forceAnimateIcons, /* focusFirstItemForAccessibility= */ false); 2210 } 2211 2212 2213 /** 2214 * Bind the items start-end from the list. 2215 * 2216 * Implementation of the method from LauncherModel.Callbacks. 2217 * 2218 * @param focusFirstItemForAccessibility true iff the first item to be added to the workspace 2219 * should be focused for accessibility. 2220 */ bindItems( final List<ItemInfo> items, final boolean forceAnimateIcons, final boolean focusFirstItemForAccessibility)2221 public void bindItems( 2222 final List<ItemInfo> items, 2223 final boolean forceAnimateIcons, 2224 final boolean focusFirstItemForAccessibility) { 2225 // Get the list of added items and intersect them with the set of items here 2226 final Collection<Animator> bounceAnims = new ArrayList<>(); 2227 final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation(); 2228 Workspace workspace = mWorkspace; 2229 int newItemsScreenId = -1; 2230 int end = items.size(); 2231 View newView = null; 2232 for (int i = 0; i < end; i++) { 2233 final ItemInfo item = items.get(i); 2234 2235 // Short circuit if we are loading dock items for a configuration which has no dock 2236 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && 2237 mHotseat == null) { 2238 continue; 2239 } 2240 2241 final View view; 2242 switch (item.itemType) { 2243 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 2244 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 2245 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: { 2246 WorkspaceItemInfo info = (WorkspaceItemInfo) item; 2247 view = createShortcut(info); 2248 break; 2249 } 2250 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: { 2251 view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, 2252 (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), 2253 (FolderInfo) item); 2254 break; 2255 } 2256 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 2257 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: { 2258 view = inflateAppWidget((LauncherAppWidgetInfo) item); 2259 if (view == null) { 2260 continue; 2261 } 2262 break; 2263 } 2264 default: 2265 throw new RuntimeException("Invalid Item Type"); 2266 } 2267 2268 /* 2269 * Remove colliding items. 2270 */ 2271 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 2272 CellLayout cl = mWorkspace.getScreenWithId(item.screenId); 2273 if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { 2274 View v = cl.getChildAt(item.cellX, item.cellY); 2275 Object tag = v.getTag(); 2276 String desc = "Collision while binding workspace item: " + item 2277 + ". Collides with " + tag; 2278 if (FeatureFlags.IS_STUDIO_BUILD) { 2279 throw (new RuntimeException(desc)); 2280 } else { 2281 Log.d(TAG, desc); 2282 getModelWriter().deleteItemFromDatabase(item); 2283 continue; 2284 } 2285 } 2286 } 2287 workspace.addInScreenFromBind(view, item); 2288 if (animateIcons) { 2289 // Animate all the applications up now 2290 view.setAlpha(0f); 2291 view.setScaleX(0f); 2292 view.setScaleY(0f); 2293 bounceAnims.add(createNewAppBounceAnimation(view, i)); 2294 newItemsScreenId = item.screenId; 2295 } 2296 2297 if (newView == null) { 2298 newView = view; 2299 } 2300 } 2301 2302 View viewToFocus = newView; 2303 // Animate to the correct pager 2304 if (animateIcons && newItemsScreenId > -1) { 2305 AnimatorSet anim = new AnimatorSet(); 2306 anim.playTogether(bounceAnims); 2307 if (focusFirstItemForAccessibility && viewToFocus != null) { 2308 anim.addListener(new AnimatorListenerAdapter() { 2309 @Override 2310 public void onAnimationEnd(Animator animation) { 2311 viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 2312 } 2313 }); 2314 } 2315 2316 int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage()); 2317 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId); 2318 final Runnable startBounceAnimRunnable = anim::start; 2319 2320 if (newItemsScreenId != currentScreenId) { 2321 // We post the animation slightly delayed to prevent slowdowns 2322 // when we are loading right after we return to launcher. 2323 mWorkspace.postDelayed(new Runnable() { 2324 public void run() { 2325 if (mWorkspace != null) { 2326 closeOpenViews(false); 2327 2328 mWorkspace.snapToPage(newScreenIndex); 2329 mWorkspace.postDelayed(startBounceAnimRunnable, 2330 NEW_APPS_ANIMATION_DELAY); 2331 } 2332 } 2333 }, NEW_APPS_PAGE_MOVE_DELAY); 2334 } else { 2335 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); 2336 } 2337 } else if (focusFirstItemForAccessibility && viewToFocus != null) { 2338 viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 2339 } 2340 workspace.requestLayout(); 2341 } 2342 2343 /** 2344 * Add the views for a widget to the workspace. 2345 */ bindAppWidget(LauncherAppWidgetInfo item)2346 public void bindAppWidget(LauncherAppWidgetInfo item) { 2347 View view = inflateAppWidget(item); 2348 if (view != null) { 2349 mWorkspace.addInScreen(view, item); 2350 mWorkspace.requestLayout(); 2351 } 2352 } 2353 inflateAppWidget(LauncherAppWidgetInfo item)2354 private View inflateAppWidget(LauncherAppWidgetInfo item) { 2355 if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { 2356 item.providerName = QsbContainerView.getSearchComponentName(this); 2357 if (item.providerName == null) { 2358 getModelWriter().deleteItemFromDatabase(item); 2359 return null; 2360 } 2361 } 2362 final AppWidgetHostView view; 2363 if (mIsSafeModeEnabled) { 2364 view = new PendingAppWidgetHostView(this, item, mIconCache, true); 2365 prepareAppWidget(view, item); 2366 return view; 2367 } 2368 2369 Object traceToken = TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); 2370 2371 try { 2372 final LauncherAppWidgetProviderInfo appWidgetInfo; 2373 String removalReason = ""; 2374 2375 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { 2376 // If the provider is not ready, bind as a pending widget. 2377 appWidgetInfo = null; 2378 removalReason = "the provider isn't ready."; 2379 } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { 2380 // The widget id is not valid. Try to find the widget based on the provider info. 2381 appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user); 2382 if (appWidgetInfo == null) { 2383 if (WidgetsModel.GO_DISABLE_WIDGETS) { 2384 removalReason = "widgets are disabled on go device."; 2385 } else { 2386 removalReason = 2387 "WidgetManagerHelper cannot find a provider from provider info."; 2388 } 2389 } 2390 } else { 2391 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId); 2392 if (appWidgetInfo == null) { 2393 if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { 2394 removalReason = 2395 "CustomWidgetManager cannot find provider from that widget id."; 2396 } else { 2397 removalReason = "AppWidgetManager cannot find provider for that widget id." 2398 + " It could be because AppWidgetService is not available, or the" 2399 + " appWidgetId has not been bound to a the provider yet, or you" 2400 + " don't have access to that appWidgetId."; 2401 } 2402 } 2403 } 2404 2405 // If the provider is ready, but the width is not yet restored, try to restore it. 2406 if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) 2407 && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { 2408 if (appWidgetInfo == null) { 2409 FileLog.d(TAG, "Removing restored widget: id=" + item.appWidgetId 2410 + " belongs to component " + item.providerName + " user " + item.user 2411 + ", as the provider is null and " + removalReason); 2412 getModelWriter().deleteItemFromDatabase(item); 2413 return null; 2414 } 2415 2416 // If we do not have a valid id, try to bind an id. 2417 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { 2418 if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { 2419 // Id has not been allocated yet. Allocate a new id. 2420 item.appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 2421 item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED; 2422 2423 // Also try to bind the widget. If the bind fails, the user will be shown 2424 // a click to setup UI, which will ask for the bind permission. 2425 PendingAddWidgetInfo pendingInfo = 2426 new PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer); 2427 pendingInfo.spanX = item.spanX; 2428 pendingInfo.spanY = item.spanY; 2429 pendingInfo.minSpanX = item.minSpanX; 2430 pendingInfo.minSpanY = item.minSpanY; 2431 Bundle options = pendingInfo.getDefaultSizeOptions(this); 2432 2433 boolean isDirectConfig = 2434 item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); 2435 if (isDirectConfig && item.bindOptions != null) { 2436 Bundle newOptions = item.bindOptions.getExtras(); 2437 if (options != null) { 2438 newOptions.putAll(options); 2439 } 2440 options = newOptions; 2441 } 2442 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 2443 item.appWidgetId, appWidgetInfo, options); 2444 2445 // We tried to bind once. If we were not able to bind, we would need to 2446 // go through the permission dialog, which means we cannot skip the config 2447 // activity. 2448 item.bindOptions = null; 2449 item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG; 2450 2451 // Bind succeeded 2452 if (success) { 2453 // If the widget has a configure activity, it is still needs to set it 2454 // up, otherwise the widget is ready to go. 2455 item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig 2456 ? LauncherAppWidgetInfo.RESTORE_COMPLETED 2457 : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 2458 } 2459 2460 getModelWriter().updateItemInDatabase(item); 2461 } 2462 } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) 2463 && (appWidgetInfo.configure == null)) { 2464 // The widget was marked as UI not ready, but there is no configure activity to 2465 // update the UI. 2466 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; 2467 getModelWriter().updateItemInDatabase(item); 2468 } 2469 else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) 2470 && appWidgetInfo.configure != null) { 2471 if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) { 2472 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; 2473 getModelWriter().updateItemInDatabase(item); 2474 } 2475 } 2476 } 2477 2478 if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { 2479 // Verify that we own the widget 2480 if (appWidgetInfo == null) { 2481 FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); 2482 getModelWriter().deleteWidgetInfo(item, getAppWidgetHost()); 2483 return null; 2484 } 2485 2486 item.minSpanX = appWidgetInfo.minSpanX; 2487 item.minSpanY = appWidgetInfo.minSpanY; 2488 view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo); 2489 } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) 2490 && appWidgetInfo != null) { 2491 mAppWidgetHost.addPendingView(item.appWidgetId, 2492 new PendingAppWidgetHostView(this, item, mIconCache, false)); 2493 view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo); 2494 } else { 2495 view = new PendingAppWidgetHostView(this, item, mIconCache, false); 2496 } 2497 prepareAppWidget(view, item); 2498 } finally { 2499 TraceHelper.INSTANCE.endSection(traceToken); 2500 } 2501 2502 return view; 2503 } 2504 2505 /** 2506 * Restores a pending widget. 2507 * 2508 * @param appWidgetId The app widget id 2509 */ completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag)2510 private LauncherAppWidgetInfo completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag) { 2511 LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId); 2512 if ((view == null) || !(view instanceof PendingAppWidgetHostView)) { 2513 Log.e(TAG, "Widget update called, when the widget no longer exists."); 2514 return null; 2515 } 2516 2517 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); 2518 info.restoreStatus = finalRestoreFlag; 2519 if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { 2520 info.pendingItemInfo = null; 2521 } 2522 2523 if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) { 2524 view.reInflate(); 2525 } 2526 2527 getModelWriter().updateItemInDatabase(info); 2528 return info; 2529 } 2530 onPageBoundSynchronously(int page)2531 public void onPageBoundSynchronously(int page) { 2532 mSynchronouslyBoundPage = page; 2533 mWorkspace.setCurrentPage(page); 2534 mPageToBindSynchronously = PagedView.INVALID_PAGE; 2535 } 2536 2537 @Override executeOnNextDraw(ViewOnDrawExecutor executor)2538 public void executeOnNextDraw(ViewOnDrawExecutor executor) { 2539 clearPendingBinds(); 2540 mPendingExecutor = executor; 2541 if (!isInState(ALL_APPS)) { 2542 mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW); 2543 mPendingExecutor.execute(() -> mAppsView.getAppsStore().disableDeferUpdates( 2544 AllAppsStore.DEFER_UPDATES_NEXT_DRAW)); 2545 } 2546 2547 executor.attachTo(this); 2548 } 2549 clearPendingExecutor(ViewOnDrawExecutor executor)2550 public void clearPendingExecutor(ViewOnDrawExecutor executor) { 2551 if (mPendingExecutor == executor) { 2552 mPendingExecutor = null; 2553 } 2554 } 2555 2556 @Override finishFirstPageBind(final ViewOnDrawExecutor executor)2557 public void finishFirstPageBind(final ViewOnDrawExecutor executor) { 2558 AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD); 2559 if (property.getValue() < 1) { 2560 ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1); 2561 if (executor != null) { 2562 anim.addListener(new AnimatorListenerAdapter() { 2563 @Override 2564 public void onAnimationEnd(Animator animation) { 2565 executor.onLoadAnimationCompleted(); 2566 } 2567 }); 2568 } 2569 anim.start(); 2570 } else if (executor != null) { 2571 executor.onLoadAnimationCompleted(); 2572 } 2573 } 2574 2575 /** 2576 * Callback saying that there aren't any more items to bind. 2577 * 2578 * Implementation of the method from LauncherModel.Callbacks. 2579 */ finishBindingItems(int pageBoundFirst)2580 public void finishBindingItems(int pageBoundFirst) { 2581 Object traceToken = TraceHelper.INSTANCE.beginSection("finishBindingItems"); 2582 mWorkspace.restoreInstanceStateForRemainingPages(); 2583 2584 setWorkspaceLoading(false); 2585 2586 if (mPendingActivityResult != null) { 2587 handleActivityResult(mPendingActivityResult.requestCode, 2588 mPendingActivityResult.resultCode, mPendingActivityResult.data); 2589 mPendingActivityResult = null; 2590 } 2591 2592 ItemInstallQueue.INSTANCE.get(this) 2593 .resumeModelPush(FLAG_LOADER_RUNNING); 2594 2595 // When undoing the removal of the last item on a page, return to that page. 2596 // Since we are just resetting the current page without user interaction, 2597 // override the previous page so we don't log the page switch. 2598 mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */); 2599 mPageToBindSynchronously = PagedView.INVALID_PAGE; 2600 2601 // Cache one page worth of icons 2602 getViewCache().setCacheSize(R.layout.folder_application, 2603 mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows); 2604 getViewCache().setCacheSize(R.layout.folder_page, 2); 2605 2606 TraceHelper.INSTANCE.endSection(traceToken); 2607 } 2608 canRunNewAppsAnimation()2609 private boolean canRunNewAppsAnimation() { 2610 if (mDragController.isDragging()) { 2611 return false; 2612 } else { 2613 return (SystemClock.uptimeMillis() - mLastTouchUpTime) 2614 > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); 2615 } 2616 } 2617 createNewAppBounceAnimation(View v, int i)2618 private ValueAnimator createNewAppBounceAnimation(View v, int i) { 2619 ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) 2620 .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION); 2621 bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY); 2622 bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); 2623 return bounceAnim; 2624 } 2625 announceForAccessibility(@tringRes int stringResId)2626 private void announceForAccessibility(@StringRes int stringResId) { 2627 getDragLayer().announceForAccessibility(getString(stringResId)); 2628 } 2629 2630 /** 2631 * Add the icons for all apps. 2632 * 2633 * Implementation of the method from LauncherModel.Callbacks. 2634 */ 2635 @Override bindAllApplications(AppInfo[] apps, int flags)2636 public void bindAllApplications(AppInfo[] apps, int flags) { 2637 mAppsView.getAppsStore().setApps(apps, flags); 2638 PopupContainerWithArrow.dismissInvalidPopup(this); 2639 } 2640 2641 /** 2642 * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary 2643 * because LauncherModel's map is updated in the background, while Launcher runs on the UI. 2644 */ 2645 @Override bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy)2646 public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) { 2647 mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy); 2648 } 2649 2650 @Override bindIncrementalDownloadProgressUpdated(AppInfo app)2651 public void bindIncrementalDownloadProgressUpdated(AppInfo app) { 2652 mAppsView.getAppsStore().updateProgressBar(app); 2653 } 2654 2655 @Override bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets)2656 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { 2657 mWorkspace.widgetsRestored(widgets); 2658 } 2659 2660 /** 2661 * Some shortcuts were updated in the background. 2662 * Implementation of the method from LauncherModel.Callbacks. 2663 * 2664 * @param updated list of shortcuts which have changed. 2665 */ 2666 @Override bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated)2667 public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { 2668 if (!updated.isEmpty()) { 2669 mWorkspace.updateShortcuts(updated); 2670 PopupContainerWithArrow.dismissInvalidPopup(this); 2671 } 2672 } 2673 2674 /** 2675 * Update the state of a package, typically related to install state. 2676 * 2677 * Implementation of the method from LauncherModel.Callbacks. 2678 */ 2679 @Override bindRestoreItemsChange(HashSet<ItemInfo> updates)2680 public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { 2681 mWorkspace.updateRestoreItems(updates); 2682 } 2683 2684 /** 2685 * A package was uninstalled/updated. We take both the super set of packageNames 2686 * in addition to specific applications to remove, the reason being that 2687 * this can be called when a package is updated as well. In that scenario, 2688 * we only remove specific components from the workspace and hotseat, where as 2689 * package-removal should clear all items by package name. 2690 */ 2691 @Override bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher)2692 public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) { 2693 mWorkspace.removeItemsByMatcher(matcher); 2694 mDragController.onAppsRemoved(matcher); 2695 PopupContainerWithArrow.dismissInvalidPopup(this); 2696 } 2697 2698 @Override bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets)2699 public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) { 2700 mPopupDataProvider.setAllWidgets(allWidgets); 2701 } 2702 2703 /** 2704 * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only 2705 * refreshes the widgets and shortcuts associated with the given package/user 2706 */ refreshAndBindWidgetsForPackageUser(@ullable PackageUserKey packageUser)2707 public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) { 2708 mModel.refreshAndBindWidgetsAndShortcuts(packageUser); 2709 } 2710 2711 /** 2712 * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all] 2713 */ 2714 @Override dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)2715 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 2716 super.dump(prefix, fd, writer, args); 2717 2718 if (args.length > 0 && TextUtils.equals(args[0], "--all")) { 2719 writer.println(prefix + "Workspace Items"); 2720 for (int i = 0; i < mWorkspace.getPageCount(); i++) { 2721 writer.println(prefix + " Homescreen " + i); 2722 2723 ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets(); 2724 for (int j = 0; j < layout.getChildCount(); j++) { 2725 Object tag = layout.getChildAt(j).getTag(); 2726 if (tag != null) { 2727 writer.println(prefix + " " + tag.toString()); 2728 } 2729 } 2730 } 2731 2732 writer.println(prefix + " Hotseat"); 2733 ViewGroup layout = mHotseat.getShortcutsAndWidgets(); 2734 for (int j = 0; j < layout.getChildCount(); j++) { 2735 Object tag = layout.getChildAt(j).getTag(); 2736 if (tag != null) { 2737 writer.println(prefix + " " + tag.toString()); 2738 } 2739 } 2740 } 2741 2742 writer.println(prefix + "Misc:"); 2743 dumpMisc(prefix + "\t", writer); 2744 writer.println(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading); 2745 writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs 2746 + " mPendingActivityResult=" + mPendingActivityResult); 2747 writer.println(prefix + "\tmRotationHelper: " + mRotationHelper); 2748 writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening()); 2749 2750 // Extra logging for general debugging 2751 mDragLayer.dump(prefix, writer); 2752 mStateManager.dump(prefix, writer); 2753 mPopupDataProvider.dump(prefix, writer); 2754 mDeviceProfile.dump(prefix, writer); 2755 2756 try { 2757 FileLog.flushAll(writer); 2758 } catch (Exception e) { 2759 // Ignore 2760 } 2761 2762 mModel.dumpState(prefix, fd, writer, args); 2763 2764 if (mLauncherCallbacks != null) { 2765 mLauncherCallbacks.dump(prefix, fd, writer, args); 2766 } 2767 mOverlayManager.dump(prefix, writer); 2768 } 2769 2770 @Override onProvideKeyboardShortcuts( List<KeyboardShortcutGroup> data, Menu menu, int deviceId)2771 public void onProvideKeyboardShortcuts( 2772 List<KeyboardShortcutGroup> data, Menu menu, int deviceId) { 2773 2774 ArrayList<KeyboardShortcutInfo> shortcutInfos = new ArrayList<>(); 2775 if (isInState(NORMAL)) { 2776 shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), 2777 KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); 2778 shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text), 2779 KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON)); 2780 } 2781 getSupportedActions(this, getCurrentFocus()).forEach(la -> 2782 shortcutInfos.add(new KeyboardShortcutInfo( 2783 la.accessibilityAction.getLabel(), la.keyCode, KeyEvent.META_CTRL_ON))); 2784 if (!shortcutInfos.isEmpty()) { 2785 data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos)); 2786 } 2787 2788 super.onProvideKeyboardShortcuts(data, menu, deviceId); 2789 } 2790 2791 @Override onKeyShortcut(int keyCode, KeyEvent event)2792 public boolean onKeyShortcut(int keyCode, KeyEvent event) { 2793 if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { 2794 switch (keyCode) { 2795 case KeyEvent.KEYCODE_A: 2796 if (isInState(NORMAL)) { 2797 getStateManager().goToState(ALL_APPS); 2798 return true; 2799 } 2800 break; 2801 case KeyEvent.KEYCODE_W: 2802 if (isInState(NORMAL)) { 2803 OptionsPopupView.openWidgets(this); 2804 return true; 2805 } 2806 break; 2807 default: 2808 for (LauncherAction la : getSupportedActions(this, getCurrentFocus())) { 2809 if (la.keyCode == keyCode) { 2810 return la.invokeFromKeyboard(getCurrentFocus()); 2811 } 2812 } 2813 } 2814 } 2815 return super.onKeyShortcut(keyCode, event); 2816 } 2817 2818 @Override onKeyUp(int keyCode, KeyEvent event)2819 public boolean onKeyUp(int keyCode, KeyEvent event) { 2820 if (keyCode == KeyEvent.KEYCODE_MENU) { 2821 // KEYCODE_MENU is sent by some tests, for example 2822 // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling. 2823 if (!mDragController.isDragging() && !mWorkspace.isSwitchingState() && 2824 isInState(NORMAL)) { 2825 // Close any open floating views. 2826 closeOpenViews(); 2827 2828 // Setting the touch point to (-1, -1) will show the options popup in the center of 2829 // the screen. 2830 if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { 2831 Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up"); 2832 } 2833 OptionsPopupView.showDefaultOptions(this, -1, -1); 2834 } 2835 return true; 2836 } 2837 return super.onKeyUp(keyCode, event); 2838 } 2839 2840 @Override collectStateHandlers(List<StateHandler> out)2841 protected void collectStateHandlers(List<StateHandler> out) { 2842 out.add(getAllAppsController()); 2843 out.add(getWorkspace()); 2844 } 2845 createTouchControllers()2846 public TouchController[] createTouchControllers() { 2847 return new TouchController[] {getDragController(), new AllAppsSwipeController(this)}; 2848 } 2849 useFadeOutAnimationForLauncherStart(CancellationSignal signal)2850 public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { } 2851 onDragLayerHierarchyChanged()2852 public void onDragLayerHierarchyChanged() { } 2853 2854 @Override returnToHomescreen()2855 public void returnToHomescreen() { 2856 super.returnToHomescreen(); 2857 getStateManager().goToState(LauncherState.NORMAL); 2858 } 2859 closeOpenViews()2860 private void closeOpenViews() { 2861 closeOpenViews(true); 2862 } 2863 closeOpenViews(boolean animate)2864 protected void closeOpenViews(boolean animate) { 2865 AbstractFloatingView.closeAllOpenViews(this, animate); 2866 } 2867 getSupportedShortcuts()2868 public Stream<SystemShortcut.Factory> getSupportedShortcuts() { 2869 return Stream.of(APP_INFO, WIDGETS, INSTALL); 2870 } 2871 createAccessibilityDelegate()2872 protected LauncherAccessibilityDelegate createAccessibilityDelegate() { 2873 return new LauncherAccessibilityDelegate(this); 2874 } 2875 2876 /** 2877 * @see LauncherState#getOverviewScaleAndOffset(Launcher) 2878 */ getNormalOverviewScaleAndOffset()2879 public float[] getNormalOverviewScaleAndOffset() { 2880 return new float[] {NO_SCALE, NO_OFFSET}; 2881 } 2882 2883 /** 2884 * @see LauncherState#getTaskbarScale(Launcher) 2885 */ getNormalTaskbarScale()2886 public float getNormalTaskbarScale() { 2887 return 1f; 2888 } 2889 getLauncher(Context context)2890 public static Launcher getLauncher(Context context) { 2891 return fromContext(context); 2892 } 2893 2894 /** 2895 * Just a wrapper around the type cast to allow easier tracking of calls. 2896 */ cast(ActivityContext activityContext)2897 public static <T extends Launcher> T cast(ActivityContext activityContext) { 2898 return (T) activityContext; 2899 } 2900 2901 /** 2902 * Cross-fades the launcher's updated appearance with its previous appearance. 2903 * 2904 * This method is used to cross-fade UI updates on activity creation, specifically dark mode 2905 * updates. 2906 */ crossFadeWithPreviousAppearance()2907 private void crossFadeWithPreviousAppearance() { 2908 NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance(); 2909 2910 if (lastInstance == null || lastInstance.snapshot == null) { 2911 return; 2912 } 2913 2914 ImageView crossFadeHelper = new ImageView(this); 2915 crossFadeHelper.setImageBitmap(lastInstance.snapshot); 2916 crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 2917 2918 InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams( 2919 InsettableFrameLayout.LayoutParams.MATCH_PARENT, 2920 InsettableFrameLayout.LayoutParams.MATCH_PARENT); 2921 2922 layoutParams.ignoreInsets = true; 2923 2924 crossFadeHelper.setLayoutParams(layoutParams); 2925 2926 getRootView().addView(crossFadeHelper); 2927 2928 crossFadeHelper 2929 .animate() 2930 .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION) 2931 .alpha(0f) 2932 .withEndAction(() -> getRootView().removeView(crossFadeHelper)) 2933 .start(); 2934 } 2935 supportsAdaptiveIconAnimation(View clickedView)2936 public boolean supportsAdaptiveIconAnimation(View clickedView) { 2937 return false; 2938 } 2939 getDefaultWorkspaceDragOptions()2940 public DragOptions getDefaultWorkspaceDragOptions() { 2941 return new DragOptions(); 2942 } 2943 2944 private static class NonConfigInstance { 2945 public Configuration config; 2946 public Bitmap snapshot; 2947 } 2948 2949 @Override getStatsLogManager()2950 public StatsLogManager getStatsLogManager() { 2951 return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId); 2952 } 2953 } 2954