RecentsActivity.java revision 04d0f756782e697a171aebbb35ae86c86b839a27
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
17package com.android.systemui.recents;
18
19import android.app.Activity;
20import android.app.ActivityOptions;
21import android.app.SearchManager;
22import android.appwidget.AppWidgetHostView;
23import android.appwidget.AppWidgetManager;
24import android.appwidget.AppWidgetProviderInfo;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.SharedPreferences;
30import android.os.Bundle;
31import android.os.UserHandle;
32import android.util.Pair;
33import android.view.KeyEvent;
34import android.view.View;
35import android.view.ViewStub;
36import android.widget.Toast;
37import com.android.systemui.R;
38import com.android.systemui.recents.model.RecentsTaskLoader;
39import com.android.systemui.recents.model.SpaceNode;
40import com.android.systemui.recents.model.TaskStack;
41import com.android.systemui.recents.views.FullscreenTransitionOverlayView;
42import com.android.systemui.recents.views.RecentsView;
43import com.android.systemui.recents.views.SystemBarScrimViews;
44import com.android.systemui.recents.views.ViewAnimation;
45
46import java.lang.reflect.InvocationTargetException;
47import java.util.ArrayList;
48
49/* Activity */
50public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
51        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
52        FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
53
54    RecentsView mRecentsView;
55    SystemBarScrimViews mScrimViews;
56    ViewStub mEmptyViewStub;
57    View mEmptyView;
58    ViewStub mFullscreenOverlayStub;
59    FullscreenTransitionOverlayView mFullScreenOverlayView;
60
61    RecentsConfiguration mConfig;
62
63    RecentsAppWidgetHost mAppWidgetHost;
64    AppWidgetProviderInfo mSearchAppWidgetInfo;
65    AppWidgetHostView mSearchAppWidgetHostView;
66
67    boolean mVisible;
68    boolean mTaskLaunched;
69
70    // Runnables to finish the Recents activity
71    FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true);
72    FinishRecentsRunnable mFinishLaunchHomeRunnable;
73
74    /**
75     * A Runnable to finish Recents either with/without a transition, and either by calling finish()
76     * or just launching the specified intent.
77     */
78    class FinishRecentsRunnable implements Runnable {
79        boolean mUseCustomFinishTransition;
80        Intent mLaunchIntent;
81        ActivityOptions mLaunchOpts;
82
83        public FinishRecentsRunnable(boolean withTransition) {
84            mUseCustomFinishTransition = withTransition;
85        }
86
87        /**
88         * Creates a finish runnable that starts the specified intent, using the given
89         * ActivityOptions.
90         */
91        public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
92            mLaunchIntent = launchIntent;
93            mLaunchOpts = opts;
94        }
95
96        @Override
97        public void run() {
98            // Mark Recents as no longer visible
99            AlternateRecentsComponent.notifyVisibilityChanged(false);
100            // Finish Recents
101            if (mLaunchIntent != null) {
102                if (mLaunchOpts != null) {
103                    startActivityAsUser(mLaunchIntent, new UserHandle(UserHandle.USER_CURRENT));
104                } else {
105                    startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(),
106                            new UserHandle(UserHandle.USER_CURRENT));
107                }
108            } else {
109                finish();
110                if (mUseCustomFinishTransition) {
111                    overridePendingTransition(R.anim.recents_to_launcher_enter,
112                            R.anim.recents_to_launcher_exit);
113                }
114            }
115        }
116    }
117
118    // Broadcast receiver to handle messages from our RecentsService
119    final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
120        @Override
121        public void onReceive(Context context, Intent intent) {
122            String action = intent.getAction();
123            if (Console.Enabled) {
124                Console.log(Constants.Log.App.SystemUIHandshake,
125                        "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
126            }
127            if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
128                if (intent.getBooleanExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
129                    // Dismiss recents, launching the focused task
130                    dismissRecentsIfVisible();
131                } else {
132                    // If we are mid-animation into Recents, then reverse it and finish
133                    if (mFullScreenOverlayView == null ||
134                            !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
135                        // Otherwise, either finish Recents, or launch Home directly
136                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
137                                null, mFinishLaunchHomeRunnable, null);
138                        mRecentsView.startExitToHomeAnimation(
139                                new ViewAnimation.TaskViewExitContext(exitTrigger));
140                    }
141                }
142            } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
143                // Try and unfilter and filtered stacks
144                if (!mRecentsView.unfilterFilteredStacks()) {
145                    // If there are no filtered stacks, dismiss recents and launch the first task
146                    dismissRecentsIfVisible();
147                }
148            } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
149                // Try and start the enter animation (or restart it on configuration changed)
150                mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
151                // Call our callback
152                onEnterAnimationTriggered();
153            }
154        }
155    };
156
157    // Broadcast receiver to handle messages from the system
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                mFinishLaunchHomeRunnable.run();
164            } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
165                // Refresh the search widget
166                refreshSearchWidget();
167            }
168        }
169    };
170
171    // Debug trigger
172    final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
173        @Override
174        public void run() {
175            onDebugModeTriggered();
176        }
177    });
178
179    /** Updates the set of recent tasks */
180    void updateRecentsTasks(Intent launchIntent) {
181        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
182        SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
183        ArrayList<TaskStack> stacks = root.getStacks();
184        if (!stacks.isEmpty()) {
185            mRecentsView.setBSP(root);
186        }
187
188        // Update the configuration based on the launch intent
189        mConfig.launchedFromHome = launchIntent.getBooleanExtra(
190                AlternateRecentsComponent.EXTRA_FROM_HOME, false);
191        mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
192                AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
193        mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
194                AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
195        mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
196                AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
197        mConfig.launchedWithNoRecentTasks = !root.hasTasks();
198
199        // Show the scrim if we animate into Recents without window transitions
200        mScrimViews.prepareEnterRecentsAnimation();
201
202        // Add the default no-recents layout
203        if (mEmptyView == null) {
204            mEmptyView = mEmptyViewStub.inflate();
205        }
206        if (mConfig.launchedWithNoRecentTasks) {
207            mEmptyView.setVisibility(View.VISIBLE);
208        } else {
209            mEmptyView.setVisibility(View.GONE);
210        }
211    }
212
213    /** Attempts to allocate and bind the search bar app widget */
214    void bindSearchBarAppWidget() {
215        if (Constants.DebugFlags.App.EnableSearchLayout) {
216            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
217
218            // Reset the host view and widget info
219            mSearchAppWidgetHostView = null;
220            mSearchAppWidgetInfo = null;
221
222            // Try and load the app widget id from the settings
223            int appWidgetId = mConfig.searchBarAppWidgetId;
224            if (appWidgetId >= 0) {
225                mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
226                if (mSearchAppWidgetInfo == null) {
227                    // If there is no actual widget associated with that id, then delete it and
228                    // prepare to bind another app widget in its place
229                    ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
230                    appWidgetId = -1;
231                }
232                if (Console.Enabled) {
233                    Console.log(Constants.Log.App.SystemUIHandshake,
234                            "[RecentsActivity|onCreate|settings|appWidgetId]",
235                            "Id: " + appWidgetId,
236                            Console.AnsiBlue);
237                }
238            }
239
240            // If there is no id, then bind a new search app widget
241            if (appWidgetId < 0) {
242                Pair<Integer, AppWidgetProviderInfo> widgetInfo =
243                        ssp.bindSearchAppWidget(mAppWidgetHost);
244                if (widgetInfo != null) {
245                    if (Console.Enabled) {
246                        Console.log(Constants.Log.App.SystemUIHandshake,
247                                "[RecentsActivity|onCreate|searchWidget]",
248                                "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
249                                Console.AnsiBlue);
250                    }
251
252                    // Save the app widget id into the settings
253                    mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
254                    mSearchAppWidgetInfo = widgetInfo.second;
255                }
256            }
257        }
258    }
259
260    /** Creates the search bar app widget view */
261    void addSearchBarAppWidgetView() {
262        if (Constants.DebugFlags.App.EnableSearchLayout) {
263            int appWidgetId = mConfig.searchBarAppWidgetId;
264            if (appWidgetId >= 0) {
265                if (Console.Enabled) {
266                    Console.log(Constants.Log.App.SystemUIHandshake,
267                            "[RecentsActivity|onCreate|addSearchAppWidgetView]",
268                            "Id: " + appWidgetId,
269                            Console.AnsiBlue);
270                }
271                mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
272                        mSearchAppWidgetInfo);
273                Bundle opts = new Bundle();
274                opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
275                        AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS);
276                mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
277                // Set the padding to 0 for this search widget
278                mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
279                mRecentsView.setSearchBar(mSearchAppWidgetHostView);
280            } else {
281                mRecentsView.setSearchBar(null);
282            }
283        }
284    }
285
286    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
287    boolean dismissRecentsIfVisible() {
288        if (mVisible) {
289            // If we are mid-animation into Recents, then reverse it and finish
290            if (mFullScreenOverlayView == null ||
291                    !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
292                // If we have a focused task, then launch that task
293                if (!mRecentsView.launchFocusedTask()) {
294                    if (mConfig.launchedFromHome) {
295                        // Just start the animation out of recents
296                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
297                                null, mFinishLaunchHomeRunnable, null);
298                        mRecentsView.startExitToHomeAnimation(
299                                new ViewAnimation.TaskViewExitContext(exitTrigger));
300                    } else {
301                        // Otherwise, try and launch the first task
302                        if (!mRecentsView.launchFirstTask()) {
303                            // If there are no tasks, then just finish recents
304                            mFinishLaunchHomeRunnable.run();
305                        }
306                    }
307                }
308            }
309            return true;
310        }
311        return false;
312    }
313
314    /** Called with the activity is first created. */
315    @Override
316    public void onCreate(Bundle savedInstanceState) {
317        super.onCreate(savedInstanceState);
318        if (Console.Enabled) {
319            Console.logDivider(Constants.Log.App.SystemUIHandshake);
320            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
321                    getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
322            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
323                    Constants.Log.App.TimeRecentsStartupKey, "onCreate");
324        }
325
326        // Initialize the loader and the configuration
327        RecentsTaskLoader.initialize(this);
328        mConfig = RecentsConfiguration.reinitialize(this);
329
330        // Create the home intent runnable
331        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
332        homeIntent.addCategory(Intent.CATEGORY_HOME);
333        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
334                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
335        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
336                ActivityOptions.makeCustomAnimation(this, R.anim.recents_to_launcher_enter,
337                        R.anim.recents_to_launcher_exit));
338
339        // Initialize the widget host (the host id is static and does not change)
340        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
341
342        // Set the Recents layout
343        setContentView(R.layout.recents);
344        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
345        mRecentsView.setCallbacks(this);
346        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
347                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
348                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
349        mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
350        mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub);
351        mScrimViews = new SystemBarScrimViews(this, mConfig);
352
353        // Update the recent tasks
354        updateRecentsTasks(getIntent());
355
356        // Prepare the screenshot transition if necessary
357        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
358            mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate();
359            mFullScreenOverlayView.setCallbacks(this);
360            mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
361        }
362
363        // Bind the search app widget when we first start up
364        bindSearchBarAppWidget();
365        // Add the search bar layout
366        addSearchBarAppWidgetView();
367
368        // Update if we are getting a configuration change
369        if (savedInstanceState != null) {
370            onConfigurationChange();
371        }
372
373        // Register the broadcast receiver to handle messages when the screen is turned off
374        IntentFilter filter = new IntentFilter();
375        filter.addAction(Intent.ACTION_SCREEN_OFF);
376        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
377        registerReceiver(mSystemBroadcastReceiver, filter);
378
379        // Register any broadcast receivers for the task loader
380        RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView);
381
382        // Private API calls to make the shadows look better
383        try {
384            Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f));
385            Utilities.setShadowProperty("ambientRatio", String.valueOf(0.5f));
386        } catch (IllegalAccessException e) {
387            e.printStackTrace();
388        } catch (InvocationTargetException e) {
389            e.printStackTrace();
390        }
391    }
392
393    void onConfigurationChange() {
394        // Try and start the enter animation (or restart it on configuration changed)
395        mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
396        // Call our callback
397        onEnterAnimationTriggered();
398    }
399
400    @Override
401    protected void onNewIntent(Intent intent) {
402        super.onNewIntent(intent);
403        // Reset the task launched flag if we encounter an onNewIntent() before onStop()
404        mTaskLaunched = false;
405
406        if (Console.Enabled) {
407            Console.logDivider(Constants.Log.App.SystemUIHandshake);
408            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
409                    intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
410            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
411                    Constants.Log.App.TimeRecentsStartupKey, "onNewIntent");
412        }
413
414        // Initialize the loader and the configuration
415        RecentsTaskLoader.initialize(this);
416        mConfig = RecentsConfiguration.reinitialize(this);
417
418        // Update the recent tasks
419        updateRecentsTasks(intent);
420
421        // Prepare the screenshot transition if necessary
422        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
423            mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
424        }
425
426        // Don't attempt to rebind the search bar widget, but just add the search bar layout
427        addSearchBarAppWidgetView();
428    }
429
430    @Override
431    protected void onStart() {
432        if (Console.Enabled) {
433            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
434                    Console.AnsiRed);
435        }
436        super.onStart();
437
438        // Register the broadcast receiver to handle messages from our service
439        IntentFilter filter = new IntentFilter();
440        filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
441        filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
442        filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
443        registerReceiver(mServiceBroadcastReceiver, filter);
444
445        // Start listening for widget package changes if there is one bound
446        if (mConfig.searchBarAppWidgetId >= 0) {
447            mAppWidgetHost.startListening(this);
448        }
449
450        mVisible = true;
451    }
452
453    @Override
454    protected void onResume() {
455        if (Console.Enabled) {
456            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
457                    Console.AnsiRed);
458        }
459        super.onResume();
460    }
461
462    @Override
463    public void onAttachedToWindow() {
464        if (Console.Enabled) {
465            Console.log(Constants.Log.App.SystemUIHandshake,
466                    "[RecentsActivity|onAttachedToWindow]", "",
467                    Console.AnsiRed);
468        }
469        super.onAttachedToWindow();
470    }
471
472    @Override
473    public void onDetachedFromWindow() {
474        if (Console.Enabled) {
475            Console.log(Constants.Log.App.SystemUIHandshake,
476                    "[RecentsActivity|onDetachedFromWindow]", "",
477                    Console.AnsiRed);
478        }
479        super.onDetachedFromWindow();
480    }
481
482    @Override
483    protected void onPause() {
484        if (Console.Enabled) {
485            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
486                    Console.AnsiRed);
487        }
488        super.onPause();
489    }
490
491    @Override
492    protected void onStop() {
493        if (Console.Enabled) {
494            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
495                    Console.AnsiRed);
496        }
497        super.onStop();
498
499        // Unregister the RecentsService receiver
500        unregisterReceiver(mServiceBroadcastReceiver);
501
502        // Stop listening for widget package changes if there was one bound
503        if (mConfig.searchBarAppWidgetId >= 0) {
504            mAppWidgetHost.stopListening();
505        }
506
507        mVisible = false;
508        mTaskLaunched = false;
509    }
510
511    @Override
512    protected void onDestroy() {
513        if (Console.Enabled) {
514            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
515                    Console.AnsiRed);
516        }
517        super.onDestroy();
518
519        // Unregister the screen off receiver
520        unregisterReceiver(mSystemBroadcastReceiver);
521        RecentsTaskLoader.getInstance().unregisterReceivers();
522    }
523
524    @Override
525    public void onTrimMemory(int level) {
526        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
527        if (loader != null) {
528            loader.onTrimMemory(level);
529        }
530    }
531
532    @Override
533    public boolean onKeyDown(int keyCode, KeyEvent event) {
534        if (keyCode == KeyEvent.KEYCODE_TAB) {
535            // Focus the next task in the stack
536            final boolean backward = event.isShiftPressed();
537            mRecentsView.focusNextTask(!backward);
538            return true;
539        }
540        // Pass through the debug trigger
541        mDebugTrigger.onKeyEvent(keyCode);
542        return super.onKeyDown(keyCode, event);
543    }
544
545    @Override
546    public void onUserInteraction() {
547        mRecentsView.onUserInteraction();
548    }
549
550    @Override
551    public void onBackPressed() {
552        // Test mode where back does not do anything
553        if (mConfig.debugModeEnabled) return;
554
555        // If we are mid-animation into Recents, then reverse it and finish
556        if (mFullScreenOverlayView == null ||
557                !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
558            // If we are currently filtering in any stacks, unfilter them first
559            if (!mRecentsView.unfilterFilteredStacks()) {
560                if (mConfig.launchedFromHome) {
561                    // Just start the animation out of recents
562                    ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
563                            null, mFinishLaunchHomeRunnable, null);
564                    mRecentsView.startExitToHomeAnimation(
565                            new ViewAnimation.TaskViewExitContext(exitTrigger));
566                } else {
567                    // Otherwise, try and launch the first task
568                    if (!mRecentsView.launchFirstTask()) {
569                        // If there are no tasks, then just finish recents
570                        mFinishLaunchHomeRunnable.run();
571                    }
572                }
573            }
574        }
575    }
576
577    /** Called when debug mode is triggered */
578    public void onDebugModeTriggered() {
579        if (mConfig.developerOptionsEnabled) {
580            SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
581            if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
582                // Disable the debug mode
583                settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
584            } else {
585                // Enable the debug mode
586                settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
587            }
588            Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion +
589                    ") toggled, please restart Recents now", Toast.LENGTH_SHORT).show();
590        }
591    }
592
593    /** Called when the enter recents animation is triggered. */
594    public void onEnterAnimationTriggered() {
595        // Animate the scrims in
596        mScrimViews.startEnterRecentsAnimation();
597    }
598
599    /**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/
600
601    @Override
602    public void onEnterAnimationComplete(boolean canceled) {
603        if (!canceled) {
604            // Reset the full screenshot transition view
605            if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
606                mFullScreenOverlayView.reset();
607            }
608
609            // XXX: We should clean up the screenshot in this case as well, but it needs to happen
610            //      after to animate up
611        }
612        // Recycle the full screen screenshot
613        AlternateRecentsComponent.consumeLastScreenshot();
614    }
615
616    /**** RecentsView.RecentsViewCallbacks Implementation ****/
617
618    @Override
619    public void onExitToHomeAnimationTriggered() {
620        // Animate the scrims out
621        mScrimViews.startExitRecentsAnimation();
622    }
623
624    @Override
625    public void onTaskLaunching() {
626        mTaskLaunched = true;
627
628        // Mark recents as no longer visible
629        AlternateRecentsComponent.notifyVisibilityChanged(false);
630    }
631
632    @Override
633    public void onLastTaskRemoved() {
634        mFinishLaunchHomeRunnable.run();
635    }
636
637    /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
638
639    @Override
640    public void refreshSearchWidget() {
641        // Load the Search widget again
642        bindSearchBarAppWidget();
643        addSearchBarAppWidgetView();
644    }
645}
646