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