• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.systemui.recents;
18 
19 import android.app.Activity;
20 import android.app.ActivityOptions;
21 import android.app.SearchManager;
22 import android.appwidget.AppWidgetHostView;
23 import android.appwidget.AppWidgetManager;
24 import android.appwidget.AppWidgetProviderInfo;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.SharedPreferences;
30 import android.os.Bundle;
31 import android.os.SystemClock;
32 import android.os.UserHandle;
33 import android.util.Pair;
34 import android.view.KeyEvent;
35 import android.view.View;
36 import android.view.ViewStub;
37 import android.widget.Toast;
38 
39 import com.android.systemui.R;
40 import com.android.systemui.recents.misc.DebugTrigger;
41 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
42 import com.android.systemui.recents.misc.SystemServicesProxy;
43 import com.android.systemui.recents.misc.Utilities;
44 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
45 import com.android.systemui.recents.model.RecentsTaskLoader;
46 import com.android.systemui.recents.model.SpaceNode;
47 import com.android.systemui.recents.model.Task;
48 import com.android.systemui.recents.model.TaskStack;
49 import com.android.systemui.recents.views.DebugOverlayView;
50 import com.android.systemui.recents.views.RecentsView;
51 import com.android.systemui.recents.views.SystemBarScrimViews;
52 import com.android.systemui.recents.views.ViewAnimation;
53 import com.android.systemui.statusbar.phone.PhoneStatusBar;
54 import com.android.systemui.SystemUIApplication;
55 
56 import java.lang.ref.WeakReference;
57 import java.lang.reflect.InvocationTargetException;
58 import java.util.ArrayList;
59 
60 /**
61  * The main Recents activity that is started from AlternateRecentsComponent.
62  */
63 public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
64         RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
65         DebugOverlayView.DebugOverlayViewCallbacks {
66 
67     RecentsConfiguration mConfig;
68     long mLastTabKeyEventTime;
69 
70     // Top level views
71     RecentsView mRecentsView;
72     SystemBarScrimViews mScrimViews;
73     ViewStub mEmptyViewStub;
74     ViewStub mDebugOverlayStub;
75     View mEmptyView;
76     DebugOverlayView mDebugOverlay;
77 
78     // Search AppWidget
79     RecentsAppWidgetHost mAppWidgetHost;
80     AppWidgetProviderInfo mSearchAppWidgetInfo;
81     AppWidgetHostView mSearchAppWidgetHostView;
82 
83     // Runnables to finish the Recents activity
84     FinishRecentsRunnable mFinishLaunchHomeRunnable;
85 
86     private PhoneStatusBar mStatusBar;
87 
88     /**
89      * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
90      * launching Home with some ActivityOptions.  Generally we always launch home when we exit
91      * Recents rather than just finishing the activity since we don't know what is behind Recents in
92      * the task stack.  The only case where we finish() directly is when we are cancelling the full
93      * screen transition from the app.
94      */
95     class FinishRecentsRunnable implements Runnable {
96         Intent mLaunchIntent;
97         ActivityOptions mLaunchOpts;
98 
99         /**
100          * Creates a finish runnable that starts the specified intent, using the given
101          * ActivityOptions.
102          */
FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts)103         public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
104             mLaunchIntent = launchIntent;
105             mLaunchOpts = opts;
106         }
107 
108         @Override
run()109         public void run() {
110             // Finish Recents
111             if (mLaunchIntent != null) {
112                 if (mLaunchOpts != null) {
113                     startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
114                 } else {
115                     startActivityAsUser(mLaunchIntent, UserHandle.CURRENT);
116                 }
117             } else {
118                 finish();
119                 overridePendingTransition(R.anim.recents_to_launcher_enter,
120                         R.anim.recents_to_launcher_exit);
121             }
122         }
123     }
124 
125     /**
126      * Broadcast receiver to handle messages from AlternateRecentsComponent.
127      */
128     final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
129         @Override
130         public void onReceive(Context context, Intent intent) {
131             String action = intent.getAction();
132             if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) {
133                 if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
134                     // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
135                     dismissRecentsToFocusedTaskOrHome(false);
136                 } else if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
137                     // Otherwise, dismiss Recents to Home
138                     dismissRecentsToHome(true);
139                 } else {
140                     // Do nothing, another activity is being launched on top of Recents
141                 }
142             } else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
143                 // If we are toggling Recents, then first unfilter any filtered stacks first
144                 dismissRecentsToFocusedTaskOrHome(true);
145             } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) {
146                 // Trigger the enter animation
147                 onEnterAnimationTriggered();
148                 // Notify the fallback receiver that we have successfully got the broadcast
149                 // See AlternateRecentsComponent.onAnimationStarted()
150                 setResultCode(Activity.RESULT_OK);
151             }
152         }
153     };
154 
155     /**
156      * Broadcast receiver to handle messages from the system
157      */
158     final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
159         @Override
160         public void onReceive(Context context, Intent intent) {
161             String action = intent.getAction();
162             if (action.equals(Intent.ACTION_SCREEN_OFF)) {
163                 // When the screen turns off, dismiss Recents to Home
164                 dismissRecentsToHome(false);
165             } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
166                 // When the search activity changes, update the Search widget
167                 refreshSearchWidget();
168             }
169         }
170     };
171 
172     /**
173      * A custom debug trigger to listen for a debug key chord.
174      */
175     final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
176         @Override
177         public void run() {
178             onDebugModeTriggered();
179         }
180     });
181 
182     /** Updates the set of recent tasks */
updateRecentsTasks(Intent launchIntent)183     void updateRecentsTasks(Intent launchIntent) {
184         // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
185         // reconstructing the task stack
186         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
187         RecentsTaskLoadPlan plan = AlternateRecentsComponent.consumeInstanceLoadPlan();
188         if (plan == null) {
189             plan = loader.createLoadPlan(this);
190         }
191 
192         // Start loading tasks according to the load plan
193         if (plan.getTaskStack() == null) {
194             loader.preloadTasks(plan, mConfig.launchedFromHome);
195         }
196         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
197         loadOpts.runningTaskId = mConfig.launchedToTaskId;
198         loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;
199         loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
200         loader.loadTasks(this, plan, loadOpts);
201 
202         SpaceNode root = plan.getSpaceNode();
203         ArrayList<TaskStack> stacks = root.getStacks();
204         boolean hasTasks = root.hasTasks();
205         if (hasTasks) {
206             mRecentsView.setTaskStacks(stacks);
207         }
208         mConfig.launchedWithNoRecentTasks = !hasTasks;
209 
210         // Create the home intent runnable
211         Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
212         homeIntent.addCategory(Intent.CATEGORY_HOME);
213         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
214                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
215         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
216             ActivityOptions.makeCustomAnimation(this,
217                 mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
218                         R.anim.recents_to_launcher_enter,
219                     mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
220                         R.anim.recents_to_launcher_exit));
221 
222         // Mark the task that is the launch target
223         int taskStackCount = stacks.size();
224         if (mConfig.launchedToTaskId != -1) {
225             for (int i = 0; i < taskStackCount; i++) {
226                 TaskStack stack = stacks.get(i);
227                 ArrayList<Task> tasks = stack.getTasks();
228                 int taskCount = tasks.size();
229                 for (int j = 0; j < taskCount; j++) {
230                     Task t = tasks.get(j);
231                     if (t.key.id == mConfig.launchedToTaskId) {
232                         t.isLaunchTarget = true;
233                         break;
234                     }
235                 }
236             }
237         }
238 
239         // Update the top level view's visibilities
240         if (mConfig.launchedWithNoRecentTasks) {
241             if (mEmptyView == null) {
242                 mEmptyView = mEmptyViewStub.inflate();
243             }
244             mEmptyView.setVisibility(View.VISIBLE);
245             mRecentsView.setSearchBarVisibility(View.GONE);
246         } else {
247             if (mEmptyView != null) {
248                 mEmptyView.setVisibility(View.GONE);
249             }
250             if (mRecentsView.hasSearchBar()) {
251                 mRecentsView.setSearchBarVisibility(View.VISIBLE);
252             } else {
253                 addSearchBarAppWidgetView();
254             }
255         }
256 
257         // Animate the SystemUI scrims into view
258         mScrimViews.prepareEnterRecentsAnimation();
259     }
260 
261     /** Attempts to allocate and bind the search bar app widget */
bindSearchBarAppWidget()262     void bindSearchBarAppWidget() {
263         if (Constants.DebugFlags.App.EnableSearchLayout) {
264             SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
265 
266             // Reset the host view and widget info
267             mSearchAppWidgetHostView = null;
268             mSearchAppWidgetInfo = null;
269 
270             // Try and load the app widget id from the settings
271             int appWidgetId = mConfig.searchBarAppWidgetId;
272             if (appWidgetId >= 0) {
273                 mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
274                 if (mSearchAppWidgetInfo == null) {
275                     // If there is no actual widget associated with that id, then delete it and
276                     // prepare to bind another app widget in its place
277                     ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
278                     appWidgetId = -1;
279                 }
280             }
281 
282             // If there is no id, then bind a new search app widget
283             if (appWidgetId < 0) {
284                 Pair<Integer, AppWidgetProviderInfo> widgetInfo =
285                         ssp.bindSearchAppWidget(mAppWidgetHost);
286                 if (widgetInfo != null) {
287                     // Save the app widget id into the settings
288                     mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
289                     mSearchAppWidgetInfo = widgetInfo.second;
290                 }
291             }
292         }
293     }
294 
295     /** Creates the search bar app widget view */
addSearchBarAppWidgetView()296     void addSearchBarAppWidgetView() {
297         if (Constants.DebugFlags.App.EnableSearchLayout) {
298             int appWidgetId = mConfig.searchBarAppWidgetId;
299             if (appWidgetId >= 0) {
300                 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
301                         mSearchAppWidgetInfo);
302                 Bundle opts = new Bundle();
303                 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
304                         AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
305                 mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
306                 // Set the padding to 0 for this search widget
307                 mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
308                 mRecentsView.setSearchBar(mSearchAppWidgetHostView);
309             } else {
310                 mRecentsView.setSearchBar(null);
311             }
312         }
313     }
314 
315     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState)316     boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
317         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
318         if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
319             // If we currently have filtered stacks, then unfilter those first
320             if (checkFilteredStackState &&
321                 mRecentsView.unfilterFilteredStacks()) return true;
322             // If we have a focused Task, launch that Task now
323             if (mRecentsView.launchFocusedTask()) return true;
324             // If we launched from Home, then return to Home
325             if (mConfig.launchedFromHome) {
326                 dismissRecentsToHomeRaw(true);
327                 return true;
328             }
329             // Otherwise, try and return to the Task that Recents was launched from
330             if (mRecentsView.launchPreviousTask()) return true;
331             // If none of the other cases apply, then just go Home
332             dismissRecentsToHomeRaw(true);
333             return true;
334         }
335         return false;
336     }
337 
338     /** Dismisses Recents directly to Home. */
dismissRecentsToHomeRaw(boolean animated)339     void dismissRecentsToHomeRaw(boolean animated) {
340         if (animated) {
341             ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
342                     null, mFinishLaunchHomeRunnable, null);
343             mRecentsView.startExitToHomeAnimation(
344                     new ViewAnimation.TaskViewExitContext(exitTrigger));
345         } else {
346             mFinishLaunchHomeRunnable.run();
347         }
348     }
349 
350     /** Dismisses Recents directly to Home if we currently aren't transitioning. */
dismissRecentsToHome(boolean animated)351     boolean dismissRecentsToHome(boolean animated) {
352         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
353         if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
354             // Return to Home
355             dismissRecentsToHomeRaw(animated);
356             return true;
357         }
358         return false;
359     }
360 
361     /** Called with the activity is first created. */
362     @Override
onCreate(Bundle savedInstanceState)363     public void onCreate(Bundle savedInstanceState) {
364         super.onCreate(savedInstanceState);
365         // For the non-primary user, ensure that the SystemServicesProxy and configuration is
366         // initialized
367         RecentsTaskLoader.initialize(this);
368         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
369         mConfig = RecentsConfiguration.reinitialize(this, ssp);
370 
371         // Initialize the widget host (the host id is static and does not change)
372         mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
373 
374         // Set the Recents layout
375         setContentView(R.layout.recents);
376         mRecentsView = (RecentsView) findViewById(R.id.recents_view);
377         mRecentsView.setCallbacks(this);
378         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
379                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
380                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
381         mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
382         mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub);
383         mScrimViews = new SystemBarScrimViews(this, mConfig);
384         mStatusBar = ((SystemUIApplication) getApplication())
385                 .getComponent(PhoneStatusBar.class);
386         inflateDebugOverlay();
387 
388         // Bind the search app widget when we first start up
389         bindSearchBarAppWidget();
390 
391         // Register the broadcast receiver to handle messages when the screen is turned off
392         IntentFilter filter = new IntentFilter();
393         filter.addAction(Intent.ACTION_SCREEN_OFF);
394         filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
395         registerReceiver(mSystemBroadcastReceiver, filter);
396 
397         // Private API calls to make the shadows look better
398         try {
399             Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f));
400         } catch (IllegalAccessException e) {
401             e.printStackTrace();
402         } catch (InvocationTargetException e) {
403             e.printStackTrace();
404         }
405     }
406 
407     /** Inflates the debug overlay if debug mode is enabled. */
inflateDebugOverlay()408     void inflateDebugOverlay() {
409         if (!Constants.DebugFlags.App.EnableDebugMode) return;
410 
411         if (mConfig.debugModeEnabled && mDebugOverlay == null) {
412             // Inflate the overlay and seek bars
413             mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
414             mDebugOverlay.setCallbacks(this);
415             mRecentsView.setDebugOverlay(mDebugOverlay);
416         }
417     }
418 
419     @Override
onNewIntent(Intent intent)420     protected void onNewIntent(Intent intent) {
421         super.onNewIntent(intent);
422         setIntent(intent);
423 
424         // Clear any debug rects
425         if (mDebugOverlay != null) {
426             mDebugOverlay.clear();
427         }
428     }
429 
430     @Override
onStart()431     protected void onStart() {
432         super.onStart();
433         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
434         SystemServicesProxy ssp = loader.getSystemServicesProxy();
435         AlternateRecentsComponent.notifyVisibilityChanged(this, ssp, true);
436 
437         // Register the broadcast receiver to handle messages from our service
438         IntentFilter filter = new IntentFilter();
439         filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY);
440         filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY);
441         filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION);
442         registerReceiver(mServiceBroadcastReceiver, filter);
443 
444         // Register any broadcast receivers for the task loader
445         loader.registerReceivers(this, mRecentsView);
446 
447         // Update the recent tasks
448         updateRecentsTasks(getIntent());
449 
450         // If this is a new instance from a configuration change, then we have to manually trigger
451         // the enter animation state
452         if (mConfig.launchedHasConfigurationChanged) {
453             onEnterAnimationTriggered();
454         }
455     }
456 
457     @Override
onStop()458     protected void onStop() {
459         super.onStop();
460         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
461         SystemServicesProxy ssp = loader.getSystemServicesProxy();
462         AlternateRecentsComponent.notifyVisibilityChanged(this, ssp, false);
463 
464         // Notify the views that we are no longer visible
465         mRecentsView.onRecentsHidden();
466 
467         // Unregister the RecentsService receiver
468         unregisterReceiver(mServiceBroadcastReceiver);
469 
470         // Unregister any broadcast receivers for the task loader
471         loader.unregisterReceivers();
472     }
473 
474     @Override
onDestroy()475     protected void onDestroy() {
476         super.onDestroy();
477 
478         // Unregister the system broadcast receivers
479         unregisterReceiver(mSystemBroadcastReceiver);
480 
481         // Stop listening for widget package changes if there was one bound
482         mAppWidgetHost.stopListening();
483     }
484 
onEnterAnimationTriggered()485     public void onEnterAnimationTriggered() {
486         // Try and start the enter animation (or restart it on configuration changed)
487         ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
488         ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
489         mRecentsView.startEnterRecentsAnimation(ctx);
490         if (mConfig.searchBarAppWidgetId >= 0) {
491             final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
492                     new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
493                             RecentsActivity.this);
494             ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
495                 @Override
496                 public void run() {
497                     // Start listening for widget package changes if there is one bound
498                     RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
499                     if (cb != null) {
500                         mAppWidgetHost.startListening(cb);
501                     }
502                 }
503             });
504         }
505 
506         // Animate the SystemUI scrim views
507         mScrimViews.startEnterRecentsAnimation();
508     }
509 
510     @Override
onTrimMemory(int level)511     public void onTrimMemory(int level) {
512         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
513         if (loader != null) {
514             loader.onTrimMemory(level);
515         }
516     }
517 
518     @Override
onKeyDown(int keyCode, KeyEvent event)519     public boolean onKeyDown(int keyCode, KeyEvent event) {
520         switch (keyCode) {
521             case KeyEvent.KEYCODE_TAB: {
522                 boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
523                         mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
524                 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
525                     // Focus the next task in the stack
526                     final boolean backward = event.isShiftPressed();
527                     mRecentsView.focusNextTask(!backward);
528                     mLastTabKeyEventTime = SystemClock.elapsedRealtime();
529                 }
530                 return true;
531             }
532             case KeyEvent.KEYCODE_DPAD_UP: {
533                 mRecentsView.focusNextTask(true);
534                 return true;
535             }
536             case KeyEvent.KEYCODE_DPAD_DOWN: {
537                 mRecentsView.focusNextTask(false);
538                 return true;
539             }
540             case KeyEvent.KEYCODE_DEL:
541             case KeyEvent.KEYCODE_FORWARD_DEL: {
542                 mRecentsView.dismissFocusedTask();
543                 return true;
544             }
545             default:
546                 break;
547         }
548         // Pass through the debug trigger
549         mDebugTrigger.onKeyEvent(keyCode);
550         return super.onKeyDown(keyCode, event);
551     }
552 
553     @Override
onUserInteraction()554     public void onUserInteraction() {
555         mRecentsView.onUserInteraction();
556     }
557 
558     @Override
onBackPressed()559     public void onBackPressed() {
560         // Test mode where back does not do anything
561         if (mConfig.debugModeEnabled) return;
562 
563         // Dismiss Recents to the focused Task or Home
564         dismissRecentsToFocusedTaskOrHome(true);
565     }
566 
567     /** Called when debug mode is triggered */
onDebugModeTriggered()568     public void onDebugModeTriggered() {
569         if (mConfig.developerOptionsEnabled) {
570             SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
571             if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
572                 // Disable the debug mode
573                 settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
574                 mConfig.debugModeEnabled = false;
575                 inflateDebugOverlay();
576                 if (mDebugOverlay != null) {
577                     mDebugOverlay.disable();
578                 }
579             } else {
580                 // Enable the debug mode
581                 settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
582                 mConfig.debugModeEnabled = true;
583                 inflateDebugOverlay();
584                 if (mDebugOverlay != null) {
585                     mDebugOverlay.enable();
586                 }
587             }
588             Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
589                 (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
590                 Toast.LENGTH_SHORT).show();
591         }
592     }
593 
594     /**** RecentsView.RecentsViewCallbacks Implementation ****/
595 
596     @Override
onExitToHomeAnimationTriggered()597     public void onExitToHomeAnimationTriggered() {
598         // Animate the SystemUI scrim views out
599         mScrimViews.startExitRecentsAnimation();
600     }
601 
602     @Override
onTaskViewClicked()603     public void onTaskViewClicked() {
604     }
605 
606     @Override
onTaskLaunchFailed()607     public void onTaskLaunchFailed() {
608         // Return to Home
609         dismissRecentsToHomeRaw(true);
610     }
611 
612     @Override
onAllTaskViewsDismissed()613     public void onAllTaskViewsDismissed() {
614         mFinishLaunchHomeRunnable.run();
615     }
616 
617     @Override
onScreenPinningRequest()618     public void onScreenPinningRequest() {
619         if (mStatusBar != null) {
620             mStatusBar.showScreenPinningRequest(false);
621         }
622     }
623 
624     /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
625 
626     @Override
refreshSearchWidget()627     public void refreshSearchWidget() {
628         bindSearchBarAppWidget();
629         addSearchBarAppWidgetView();
630     }
631 
632     /**** DebugOverlayView.DebugOverlayViewCallbacks ****/
633 
634     @Override
onPrimarySeekBarChanged(float progress)635     public void onPrimarySeekBarChanged(float progress) {
636         // Do nothing
637     }
638 
639     @Override
onSecondarySeekBarChanged(float progress)640     public void onSecondarySeekBarChanged(float progress) {
641         // Do nothing
642     }
643 }
644