RecentsActivity.java revision 743d5c95f3a107639c0ff22f099cab2624da3e27
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.startOnExitAnimation(
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.startOnEnterAnimation(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        // Update the configuration based on the launch intent
166        mConfig.launchedFromHome = launchIntent.getBooleanExtra(
167                AlternateRecentsComponent.EXTRA_FROM_HOME, false);
168        mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
169                AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
170        mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
171                AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
172        mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
173                AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
174
175        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
176        SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
177        ArrayList<TaskStack> stacks = root.getStacks();
178        if (!stacks.isEmpty()) {
179            mRecentsView.setBSP(root);
180        }
181
182        if (mConfig.shouldAnimateNavBarScrim()) {
183            // Hide the scrim if we animate into Recents with window transitions
184            mNavBarScrimView.setVisibility(View.INVISIBLE);
185        } else {
186            // Show the scrim if we animate into Recents without window transitions
187            mNavBarScrimView.setVisibility(View.VISIBLE);
188        }
189
190        // Add the default no-recents layout
191        if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
192            mEmptyView.setVisibility(View.VISIBLE);
193        } else {
194            mEmptyView.setVisibility(View.GONE);
195        }
196
197        // Dim the background
198        mRecentsView.setBackgroundColor(0x80000000);
199    }
200
201    /** Attempts to allocate and bind the search bar app widget */
202    void bindSearchBarAppWidget() {
203        if (Constants.DebugFlags.App.EnableSearchLayout) {
204            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
205
206            // Reset the host view and widget info
207            mSearchAppWidgetHostView = null;
208            mSearchAppWidgetInfo = null;
209
210            // Try and load the app widget id from the settings
211            int appWidgetId = mConfig.searchBarAppWidgetId;
212            if (appWidgetId >= 0) {
213                mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
214                if (mSearchAppWidgetInfo == null) {
215                    // If there is no actual widget associated with that id, then delete it and
216                    // prepare to bind another app widget in its place
217                    ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
218                    appWidgetId = -1;
219                }
220                if (Console.Enabled) {
221                    Console.log(Constants.Log.App.SystemUIHandshake,
222                            "[RecentsActivity|onCreate|settings|appWidgetId]",
223                            "Id: " + appWidgetId,
224                            Console.AnsiBlue);
225                }
226            }
227
228            // If there is no id, then bind a new search app widget
229            if (appWidgetId < 0) {
230                Pair<Integer, AppWidgetProviderInfo> widgetInfo =
231                        ssp.bindSearchAppWidget(mAppWidgetHost);
232                if (widgetInfo != null) {
233                    if (Console.Enabled) {
234                        Console.log(Constants.Log.App.SystemUIHandshake,
235                                "[RecentsActivity|onCreate|searchWidget]",
236                                "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
237                                Console.AnsiBlue);
238                    }
239
240                    // Save the app widget id into the settings
241                    mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
242                    mSearchAppWidgetInfo = widgetInfo.second;
243                }
244            }
245        }
246    }
247
248    /** Creates the search bar app widget view */
249    void addSearchBarAppWidgetView() {
250        if (Constants.DebugFlags.App.EnableSearchLayout) {
251            int appWidgetId = mConfig.searchBarAppWidgetId;
252            if (appWidgetId >= 0) {
253                if (Console.Enabled) {
254                    Console.log(Constants.Log.App.SystemUIHandshake,
255                            "[RecentsActivity|onCreate|addSearchAppWidgetView]",
256                            "Id: " + appWidgetId,
257                            Console.AnsiBlue);
258                }
259                mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
260                        mSearchAppWidgetInfo);
261                Bundle opts = new Bundle();
262                opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
263                        AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS);
264                mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
265                // Set the padding to 0 for this search widget
266                mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
267                mRecentsView.setSearchBar(mSearchAppWidgetHostView);
268            } else {
269                mRecentsView.setSearchBar(null);
270            }
271        }
272    }
273
274    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
275    boolean dismissRecentsIfVisible() {
276        if (mVisible) {
277            // If we are mid-animation into Recents, then reverse it and finish
278            if (mFullScreenshotView == null ||
279                    !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
280                // If we have a focused task, then launch that task
281                if (!mRecentsView.launchFocusedTask()) {
282                    // If there are any tasks, then launch the first task
283                    if (!mRecentsView.launchFirstTask()) {
284                        // We really shouldn't hit this, but if we do, just animate out (aka. finish)
285                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
286                                null, mFinishRunnable, null);
287                        mRecentsView.startOnExitAnimation(
288                                new ViewAnimation.TaskViewExitContext(exitTrigger));
289                    }
290                }
291            }
292            return true;
293        }
294        return false;
295    }
296
297    /** Called with the activity is first created. */
298    @Override
299    public void onCreate(Bundle savedInstanceState) {
300        super.onCreate(savedInstanceState);
301        if (Console.Enabled) {
302            Console.logDivider(Constants.Log.App.SystemUIHandshake);
303            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
304                    getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
305            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
306                    Constants.Log.App.TimeRecentsStartupKey, "onCreate");
307        }
308
309        // Initialize the loader and the configuration
310        RecentsTaskLoader.initialize(this);
311        mConfig = RecentsConfiguration.reinitialize(this);
312
313        // Initialize the widget host (the host id is static and does not change)
314        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
315
316        // Create the view hierarchy
317        mRecentsView = new RecentsView(this);
318        mRecentsView.setCallbacks(this);
319        mRecentsView.setLayoutParams(new FrameLayout.LayoutParams(
320                FrameLayout.LayoutParams.MATCH_PARENT,
321                FrameLayout.LayoutParams.MATCH_PARENT));
322        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
323                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
324                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
325
326        // Create the empty view
327        LayoutInflater inflater = LayoutInflater.from(this);
328        mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
329        mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
330        mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
331                ViewGroup.LayoutParams.MATCH_PARENT,
332                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
333        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
334            mFullScreenshotView = new FullScreenTransitionView(this, this);
335            mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
336                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
337        }
338
339        mContainerView = new FrameLayout(this);
340        mContainerView.addView(mRecentsView);
341        mContainerView.addView(mEmptyView);
342        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
343            mContainerView.addView(mFullScreenshotView);
344        }
345        mContainerView.addView(mNavBarScrimView);
346        setContentView(mContainerView);
347
348        // Update the recent tasks
349        updateRecentsTasks(getIntent());
350
351        // Prepare the screenshot transition if necessary
352        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
353            mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
354        }
355
356        // Bind the search app widget when we first start up
357        bindSearchBarAppWidget();
358        // Add the search bar layout
359        addSearchBarAppWidgetView();
360
361        // Update if we are getting a configuration change
362        if (savedInstanceState != null) {
363            onConfigurationChange();
364        }
365
366        // XXX: Update the shadows
367        try {
368            sPropertyMethod.invoke(null, "ambientShadowStrength", String.valueOf(35f));
369            sPropertyMethod.invoke(null, "ambientRatio", String.valueOf(0.5f));
370        } catch (IllegalAccessException e) {
371            e.printStackTrace();
372        } catch (InvocationTargetException e) {
373            e.printStackTrace();
374        }
375    }
376
377    void onConfigurationChange() {
378        // Try and start the enter animation (or restart it on configuration changed)
379        mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
380        // Call our callback
381        onEnterAnimationTriggered();
382    }
383
384    @Override
385    protected void onNewIntent(Intent intent) {
386        super.onNewIntent(intent);
387        // Reset the task launched flag if we encounter an onNewIntent() before onStop()
388        mTaskLaunched = false;
389
390        if (Console.Enabled) {
391            Console.logDivider(Constants.Log.App.SystemUIHandshake);
392            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
393                    intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
394            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
395                    Constants.Log.App.TimeRecentsStartupKey, "onNewIntent");
396        }
397
398        // Initialize the loader and the configuration
399        RecentsTaskLoader.initialize(this);
400        mConfig = RecentsConfiguration.reinitialize(this);
401
402        // Update the recent tasks
403        updateRecentsTasks(intent);
404
405        // Prepare the screenshot transition if necessary
406        if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
407            mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
408        }
409
410        // Don't attempt to rebind the search bar widget, but just add the search bar layout
411        addSearchBarAppWidgetView();
412    }
413
414    @Override
415    protected void onStart() {
416        if (Console.Enabled) {
417            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
418                    Console.AnsiRed);
419        }
420        super.onStart();
421
422        // Start listening for widget package changes if there is one bound
423        if (mConfig.searchBarAppWidgetId >= 0) {
424            mAppWidgetHost.startListening();
425        }
426
427        mVisible = true;
428    }
429
430    @Override
431    protected void onResume() {
432        if (Console.Enabled) {
433            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
434                    Console.AnsiRed);
435        }
436        super.onResume();
437    }
438
439    @Override
440    public void onAttachedToWindow() {
441        if (Console.Enabled) {
442            Console.log(Constants.Log.App.SystemUIHandshake,
443                    "[RecentsActivity|onAttachedToWindow]", "",
444                    Console.AnsiRed);
445        }
446        super.onAttachedToWindow();
447
448        // Register the broadcast receiver to handle messages from our service
449        IntentFilter filter = new IntentFilter();
450        filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
451        filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
452        filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
453        registerReceiver(mServiceBroadcastReceiver, filter);
454
455        // Register the broadcast receiver to handle messages when the screen is turned off
456        filter = new IntentFilter();
457        filter.addAction(Intent.ACTION_SCREEN_OFF);
458        registerReceiver(mScreenOffReceiver, filter);
459
460        // Register any broadcast receivers for the task loader
461        RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView);
462    }
463
464    @Override
465    public void onDetachedFromWindow() {
466        if (Console.Enabled) {
467            Console.log(Constants.Log.App.SystemUIHandshake,
468                    "[RecentsActivity|onDetachedFromWindow]", "",
469                    Console.AnsiRed);
470        }
471        super.onDetachedFromWindow();
472
473        // Unregister any broadcast receivers we have registered
474        unregisterReceiver(mServiceBroadcastReceiver);
475        unregisterReceiver(mScreenOffReceiver);
476        RecentsTaskLoader.getInstance().unregisterReceivers();
477    }
478
479    @Override
480    protected void onPause() {
481        if (Console.Enabled) {
482            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
483                    Console.AnsiRed);
484        }
485        super.onPause();
486    }
487
488    @Override
489    protected void onStop() {
490        if (Console.Enabled) {
491            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
492                    Console.AnsiRed);
493        }
494        super.onStop();
495
496        // Stop listening for widget package changes if there was one bound
497        if (mConfig.searchBarAppWidgetId >= 0) {
498            mAppWidgetHost.stopListening();
499        }
500
501        mVisible = false;
502        mTaskLaunched = false;
503    }
504
505    @Override
506    protected void onDestroy() {
507        if (Console.Enabled) {
508            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
509                    Console.AnsiRed);
510        }
511        super.onDestroy();
512    }
513
514    @Override
515    public void onTrimMemory(int level) {
516        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
517        if (loader != null) {
518            loader.onTrimMemory(level);
519        }
520    }
521
522    @Override
523    public boolean onKeyDown(int keyCode, KeyEvent event) {
524        if (keyCode == KeyEvent.KEYCODE_TAB) {
525            // Focus the next task in the stack
526            final boolean backward = event.isShiftPressed();
527            mRecentsView.focusNextTask(!backward);
528            return true;
529        }
530
531        return super.onKeyDown(keyCode, event);
532    }
533
534    @Override
535    public void onUserInteraction() {
536        mRecentsView.onUserInteraction();
537    }
538
539    @Override
540    public void onBackPressed() {
541        // If we are mid-animation into Recents, then reverse it and finish
542        if (mFullScreenshotView == null ||
543                !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
544            // If we are currently filtering in any stacks, unfilter them first
545            if (!mRecentsView.unfilterFilteredStacks()) {
546                if (mConfig.launchedFromHome) {
547                    // Just start the animation out of recents
548                    ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
549                            null, mFinishRunnable, null);
550                    mRecentsView.startOnExitAnimation(
551                            new ViewAnimation.TaskViewExitContext(exitTrigger));
552                } else {
553                    // Otherwise, try and launch the first task
554                    if (!mRecentsView.launchFirstTask()) {
555                        // If there are no tasks, then just finish recents
556                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
557                                null, mFinishRunnable, null);
558                        mRecentsView.startOnExitAnimation(
559                                new ViewAnimation.TaskViewExitContext(exitTrigger));
560                    }
561                }
562            }
563        }
564    }
565
566    public void onEnterAnimationTriggered() {
567        // Fade in the scrim
568        if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
569            mNavBarScrimView.setVisibility(View.VISIBLE);
570            mNavBarScrimView.setAlpha(0f);
571            mNavBarScrimView.animate().alpha(1f)
572                    .setStartDelay(mConfig.taskBarEnterAnimDelay)
573                    .setDuration(mConfig.navBarScrimEnterDuration)
574                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
575                    .withLayer()
576                    .start();
577        }
578    }
579
580    @Override
581    public void onEnterAnimationComplete(boolean canceled) {
582        if (!canceled) {
583            // Reset the full screenshot transition view
584            if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
585                mFullScreenshotView.reset();
586            }
587
588            // XXX: We should clean up the screenshot in this case as well, but it needs to happen
589            //      after to animate up
590        }
591        // Recycle the full screen screenshot
592        AlternateRecentsComponent.consumeLastScreenshot();
593    }
594
595    @Override
596    public void onTaskLaunching(boolean isTaskInStackBounds) {
597        mTaskLaunched = true;
598
599        // Fade out the scrim
600        if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
601            mNavBarScrimView.animate().alpha(0f)
602                    .setStartDelay(0)
603                    .setDuration(mConfig.taskBarExitAnimDuration)
604                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
605                    .withLayer()
606                    .start();
607        }
608
609        // Mark recents as no longer visible
610        AlternateRecentsComponent.notifyVisibilityChanged(false);
611    }
612
613    @Override
614    public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
615        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
616        if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
617            // The search provider may have changed, so just delete the old widget and bind it again
618            ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
619            mConfig.updateSearchBarAppWidgetId(this, -1);
620            // Load the widget again
621            bindSearchBarAppWidget();
622            addSearchBarAppWidgetView();
623        }
624    }
625}
626