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