RecentsActivity.java revision 190fe3bf88388fcb109af64571e3baa0d01f1c37
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.app.TaskStackBuilder;
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.pm.ActivityInfo;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.SystemClock;
33import android.os.UserHandle;
34import android.provider.Settings;
35import android.view.KeyEvent;
36import android.view.View;
37import android.view.ViewStub;
38import com.android.internal.logging.MetricsLogger;
39import com.android.systemui.R;
40import com.android.systemui.recents.events.EventBus;
41import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
42import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
43import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
44import com.android.systemui.recents.events.ui.DismissTaskEvent;
45import com.android.systemui.recents.events.ui.ResizeTaskEvent;
46import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
47import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
48import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
49import com.android.systemui.recents.misc.Console;
50import com.android.systemui.recents.misc.ReferenceCountedTrigger;
51import com.android.systemui.recents.misc.SystemServicesProxy;
52import com.android.systemui.recents.model.RecentsPackageMonitor;
53import com.android.systemui.recents.model.RecentsTaskLoadPlan;
54import com.android.systemui.recents.model.RecentsTaskLoader;
55import com.android.systemui.recents.model.Task;
56import com.android.systemui.recents.model.TaskStack;
57import com.android.systemui.recents.views.RecentsView;
58import com.android.systemui.recents.views.SystemBarScrimViews;
59import com.android.systemui.recents.views.ViewAnimation;
60
61import java.util.ArrayList;
62
63/**
64 * The main Recents activity that is started from AlternateRecentsComponent.
65 */
66public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
67
68    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
69
70    RecentsConfiguration mConfig;
71    RecentsPackageMonitor mPackageMonitor;
72    long mLastTabKeyEventTime;
73
74    // Top level views
75    RecentsView mRecentsView;
76    SystemBarScrimViews mScrimViews;
77    ViewStub mEmptyViewStub;
78    View mEmptyView;
79
80    // Resize task debug
81    RecentsResizeTaskDialog mResizeTaskDebugDialog;
82
83    // Search AppWidget
84    AppWidgetProviderInfo mSearchWidgetInfo;
85    RecentsAppWidgetHost mAppWidgetHost;
86    RecentsAppWidgetHostView mSearchWidgetHostView;
87
88    // Runnables to finish the Recents activity
89    FinishRecentsRunnable mFinishLaunchHomeRunnable;
90
91    // Runnable to be executed after we paused ourselves
92    Runnable mAfterPauseRunnable;
93
94    /**
95     * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
96     * launching Home with some ActivityOptions.  Generally we always launch home when we exit
97     * Recents rather than just finishing the activity since we don't know what is behind Recents in
98     * the task stack.  The only case where we finish() directly is when we are cancelling the full
99     * screen transition from the app.
100     */
101    class FinishRecentsRunnable implements Runnable {
102        Intent mLaunchIntent;
103        ActivityOptions mLaunchOpts;
104
105        /**
106         * Creates a finish runnable that starts the specified intent, using the given
107         * ActivityOptions.
108         */
109        public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
110            mLaunchIntent = launchIntent;
111            mLaunchOpts = opts;
112        }
113
114        @Override
115        public void run() {
116            try {
117                startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
118            } catch (Exception e) {
119                Console.logError(RecentsActivity.this,
120                        getString(R.string.recents_launch_error_message, "Home"));
121            }
122        }
123    }
124
125    /**
126     * Broadcast receiver to handle messages from AlternateRecentsComponent.
127     */
128    final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
129        @Override
130        public void onReceive(Context context, Intent intent) {
131            String action = intent.getAction();
132            if (action.equals(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY)) {
133                if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
134                    // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
135                    dismissRecentsToFocusedTaskOrHome(false);
136                } else if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
137                    // Otherwise, dismiss Recents to Home
138                    dismissRecentsToHome(true);
139                } else {
140                    // Do nothing
141                }
142            } else if (action.equals(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
143                // If we are toggling Recents, then first unfilter any filtered stacks first
144                dismissRecentsToFocusedTaskOrHome(true);
145            } else if (action.equals(RecentsImpl.ACTION_START_ENTER_ANIMATION)) {
146                // Trigger the enter animation
147                onEnterAnimationTriggered();
148                // Notify the fallback receiver that we have successfully got the broadcast
149                // See AlternateRecentsComponent.onAnimationStarted()
150                setResultCode(Activity.RESULT_OK);
151            }
152        }
153    };
154
155    /**
156     * Broadcast receiver to handle messages from the system
157     */
158    final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
159        @Override
160        public void onReceive(Context context, Intent intent) {
161            String action = intent.getAction();
162            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
163                // When the screen turns off, dismiss Recents to Home
164                dismissRecentsToHomeIfVisible(false);
165            } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
166                // When the search activity changes, update the search widget view
167                SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
168                mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(context, mAppWidgetHost);
169                refreshSearchWidgetView();
170            }
171        }
172    };
173
174    /** Updates the set of recent tasks */
175    void updateRecentsTasks() {
176        // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
177        // reconstructing the task stack
178        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
179        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
180        if (plan == null) {
181            plan = loader.createLoadPlan(this);
182        }
183
184        // Start loading tasks according to the load plan
185        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
186        if (!plan.hasTasks()) {
187            loader.preloadTasks(plan, launchState.launchedFromHome);
188        }
189        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
190        loadOpts.runningTaskId = launchState.launchedToTaskId;
191        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
192        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
193        loader.loadTasks(this, plan, loadOpts);
194
195        TaskStack stack = plan.getTaskStack();
196        launchState.launchedWithNoRecentTasks = !plan.hasTasks();
197        if (!launchState.launchedWithNoRecentTasks) {
198            mRecentsView.setTaskStack(stack);
199        }
200
201        // Create the home intent runnable
202        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
203        homeIntent.addCategory(Intent.CATEGORY_HOME);
204        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
205                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
206        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
207            ActivityOptions.makeCustomAnimation(this,
208                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
209                        R.anim.recents_to_launcher_enter,
210                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
211                        R.anim.recents_to_launcher_exit));
212
213        // Mark the task that is the launch target
214        int launchTaskIndexInStack = 0;
215        if (launchState.launchedToTaskId != -1) {
216            ArrayList<Task> tasks = stack.getTasks();
217            int taskCount = tasks.size();
218            for (int j = 0; j < taskCount; j++) {
219                Task t = tasks.get(j);
220                if (t.key.id == launchState.launchedToTaskId) {
221                    t.isLaunchTarget = true;
222                    launchTaskIndexInStack = tasks.size() - j - 1;
223                    break;
224                }
225            }
226        }
227
228        // Update the top level view's visibilities
229        if (launchState.launchedWithNoRecentTasks) {
230            if (mEmptyView == null) {
231                mEmptyView = mEmptyViewStub.inflate();
232            }
233            mEmptyView.setVisibility(View.VISIBLE);
234            mRecentsView.setSearchBarVisibility(View.GONE);
235        } else {
236            if (mEmptyView != null) {
237                mEmptyView.setVisibility(View.GONE);
238            }
239            if (mRecentsView.hasValidSearchBar()) {
240                mRecentsView.setSearchBarVisibility(View.VISIBLE);
241            } else {
242                refreshSearchWidgetView();
243            }
244        }
245
246        // Animate the SystemUI scrims into view
247        mScrimViews.prepareEnterRecentsAnimation();
248
249        // Keep track of whether we launched from the nav bar button or via alt-tab
250        if (launchState.launchedWithAltTab) {
251            MetricsLogger.count(this, "overview_trigger_alttab", 1);
252        } else {
253            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
254        }
255        // Keep track of whether we launched from an app or from home
256        if (launchState.launchedFromAppWithThumbnail) {
257            MetricsLogger.count(this, "overview_source_app", 1);
258            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
259            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
260        } else {
261            MetricsLogger.count(this, "overview_source_home", 1);
262        }
263        // Keep track of the total stack task count
264        int taskCount = stack.getTaskCount();
265        MetricsLogger.histogram(this, "overview_task_count", taskCount);
266    }
267
268    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
269    boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
270        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
271        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
272        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
273            // If we currently have filtered stacks, then unfilter those first
274            if (checkFilteredStackState &&
275                mRecentsView.unfilterFilteredStacks()) return true;
276            // If we have a focused Task, launch that Task now
277            if (mRecentsView.launchFocusedTask()) return true;
278            // If we launched from Home, then return to Home
279            if (launchState.launchedFromHome) {
280                dismissRecentsToHome(true);
281                return true;
282            }
283            // Otherwise, try and return to the Task that Recents was launched from
284            if (mRecentsView.launchPreviousTask()) return true;
285            // If none of the other cases apply, then just go Home
286            dismissRecentsToHome(true);
287            return true;
288        }
289        return false;
290    }
291
292    /**
293     * Dismisses Recents directly to Home without checking whether it is currently visible.
294     */
295    void dismissRecentsToHome(boolean animated) {
296        if (animated) {
297            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
298                    null, mFinishLaunchHomeRunnable, null);
299            mRecentsView.startExitToHomeAnimation(
300                    new ViewAnimation.TaskViewExitContext(exitTrigger));
301            mScrimViews.startExitRecentsAnimation();
302        } else {
303            mFinishLaunchHomeRunnable.run();
304        }
305    }
306
307    /** Dismisses Recents directly to Home without transition animation. */
308    void dismissRecentsToHomeWithoutTransitionAnimation() {
309        finish();
310        overridePendingTransition(0, 0);
311    }
312
313    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
314    boolean dismissRecentsToHomeIfVisible(boolean animated) {
315        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
316        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
317            // Return to Home
318            dismissRecentsToHome(animated);
319            return true;
320        }
321        return false;
322    }
323
324    /** Called with the activity is first created. */
325    @Override
326    public void onCreate(Bundle savedInstanceState) {
327        super.onCreate(savedInstanceState);
328
329        // Register this activity with the event bus
330        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
331
332        // For the non-primary user, ensure that the SystemServicesProxy and configuration is
333        // initialized
334        RecentsTaskLoader.initialize(this);
335        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
336        mConfig = RecentsConfiguration.initialize(this, ssp);
337        mConfig.update(this, ssp, ssp.getWindowRect());
338        mPackageMonitor = new RecentsPackageMonitor();
339
340        // Initialize the widget host (the host id is static and does not change)
341        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
342
343        // Set the Recents layout
344        setContentView(R.layout.recents);
345        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
346        mRecentsView.setCallbacks(this);
347        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
348                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
349                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
350        mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
351        mScrimViews = new SystemBarScrimViews(this);
352
353        // Bind the search app widget when we first start up
354        mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
355
356        // Register the broadcast receiver to handle messages when the screen is turned off
357        IntentFilter filter = new IntentFilter();
358        filter.addAction(Intent.ACTION_SCREEN_OFF);
359        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
360        registerReceiver(mSystemBroadcastReceiver, filter);
361    }
362
363    @Override
364    protected void onNewIntent(Intent intent) {
365        super.onNewIntent(intent);
366        setIntent(intent);
367    }
368
369    @Override
370    protected void onStart() {
371        super.onStart();
372        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
373        MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
374        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
375        SystemServicesProxy ssp = loader.getSystemServicesProxy();
376
377        // Notify that recents is now visible
378        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
379
380        // Register the broadcast receiver to handle messages from our service
381        IntentFilter filter = new IntentFilter();
382        filter.addAction(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY);
383        filter.addAction(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY);
384        filter.addAction(RecentsImpl.ACTION_START_ENTER_ANIMATION);
385        registerReceiver(mServiceBroadcastReceiver, filter);
386
387        // Register any broadcast receivers for the task loader
388        mPackageMonitor.register(this);
389
390        // Update the recent tasks
391        updateRecentsTasks();
392
393        // If this is a new instance from a configuration change, then we have to manually trigger
394        // the enter animation state, or if recents was relaunched by AM, without going through
395        // the normal mechanisms
396        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
397                !launchState.launchedFromAppWithThumbnail;
398        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
399            onEnterAnimationTriggered();
400        }
401
402        if (!launchState.launchedHasConfigurationChanged) {
403            mRecentsView.disableLayersForOneFrame();
404        }
405    }
406
407    @Override
408    protected void onPause() {
409        super.onPause();
410        if (mAfterPauseRunnable != null) {
411            mRecentsView.post(mAfterPauseRunnable);
412            mAfterPauseRunnable = null;
413        }
414    }
415
416    @Override
417    protected void onStop() {
418        super.onStop();
419        MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
420        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
421        SystemServicesProxy ssp = loader.getSystemServicesProxy();
422
423        // Notify that recents is now hidden
424        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
425
426        // Notify the views that we are no longer visible
427        mRecentsView.onRecentsHidden();
428
429        // Unregister the RecentsService receiver
430        unregisterReceiver(mServiceBroadcastReceiver);
431
432        // Unregister any broadcast receivers for the task loader
433        mPackageMonitor.unregister();
434
435        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
436        // through SystemUI, we need to reset the config launch flags to ensure that we do not
437        // wait on the system to send a signal that was never queued.
438        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
439        launchState.launchedFromHome = false;
440        launchState.launchedFromSearchHome = false;
441        launchState.launchedFromAppWithThumbnail = false;
442        launchState.launchedToTaskId = -1;
443        launchState.launchedWithAltTab = false;
444        launchState.launchedHasConfigurationChanged = false;
445    }
446
447    @Override
448    protected void onDestroy() {
449        super.onDestroy();
450
451        // Unregister the system broadcast receivers
452        unregisterReceiver(mSystemBroadcastReceiver);
453
454        // Stop listening for widget package changes if there was one bound
455        mAppWidgetHost.stopListening();
456        EventBus.getDefault().unregister(this);
457    }
458
459    public void onEnterAnimationTriggered() {
460        // Try and start the enter animation (or restart it on configuration changed)
461        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
462        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
463        mRecentsView.startEnterRecentsAnimation(ctx);
464
465        if (mSearchWidgetInfo != null) {
466            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
467                @Override
468                public void run() {
469                    // Start listening for widget package changes if there is one bound
470                    if (mAppWidgetHost != null) {
471                        mAppWidgetHost.startListening();
472                    }
473                }
474            });
475        }
476
477        // Animate the SystemUI scrim views
478        mScrimViews.startEnterRecentsAnimation();
479    }
480
481    @Override
482    public void onTrimMemory(int level) {
483        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
484        if (loader != null) {
485            loader.onTrimMemory(level);
486        }
487    }
488
489    @Override
490    public boolean onKeyDown(int keyCode, KeyEvent event) {
491        switch (keyCode) {
492            case KeyEvent.KEYCODE_TAB: {
493                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
494                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
495                        mLastTabKeyEventTime) > altTabKeyDelay;
496                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
497                    // Focus the next task in the stack
498                    final boolean backward = event.isShiftPressed();
499                    mRecentsView.focusNextTask(!backward);
500                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
501                }
502                return true;
503            }
504            case KeyEvent.KEYCODE_DPAD_UP: {
505                mRecentsView.focusNextTask(true);
506                return true;
507            }
508            case KeyEvent.KEYCODE_DPAD_DOWN: {
509                mRecentsView.focusNextTask(false);
510                return true;
511            }
512            case KeyEvent.KEYCODE_DEL:
513            case KeyEvent.KEYCODE_FORWARD_DEL: {
514                mRecentsView.dismissFocusedTask();
515                // Keep track of deletions by keyboard
516                MetricsLogger.histogram(this, "overview_task_dismissed_source",
517                        Constants.Metrics.DismissSourceKeyboard);
518                return true;
519            }
520            default:
521                break;
522        }
523        return super.onKeyDown(keyCode, event);
524    }
525
526    @Override
527    public void onUserInteraction() {
528        mRecentsView.onUserInteraction();
529    }
530
531    @Override
532    public void onBackPressed() {
533        // Dismiss Recents to the focused Task or Home
534        dismissRecentsToFocusedTaskOrHome(true);
535    }
536
537    /**** RecentsResizeTaskDialog ****/
538
539    private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
540        if (mResizeTaskDebugDialog == null) {
541            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
542        }
543        return mResizeTaskDebugDialog;
544    }
545
546    /**** RecentsView.RecentsViewCallbacks Implementation ****/
547
548    @Override
549    public void onExitToHomeAnimationTriggered() {
550        // Animate the SystemUI scrim views out
551        mScrimViews.startExitRecentsAnimation();
552    }
553
554    @Override
555    public void onTaskViewClicked() {
556    }
557
558    @Override
559    public void onTaskLaunchFailed() {
560        // Return to Home
561        dismissRecentsToHome(true);
562    }
563
564    @Override
565    public void onAllTaskViewsDismissed() {
566        mFinishLaunchHomeRunnable.run();
567    }
568
569    @Override
570    public void onScreenPinningRequest() {
571        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
572        SystemServicesProxy ssp = loader.getSystemServicesProxy();
573        EventBus.getDefault().send(new ScreenPinningRequestEvent(this, ssp));
574
575        MetricsLogger.count(this, "overview_screen_pinned", 1);
576    }
577
578    @Override
579    public void runAfterPause(Runnable r) {
580        mAfterPauseRunnable = r;
581    }
582
583    /**** EventBus events ****/
584
585    public final void onBusEvent(AppWidgetProviderChangedEvent event) {
586        refreshSearchWidgetView();
587    }
588
589    public final void onBusEvent(ShowApplicationInfoEvent event) {
590        // Create a new task stack with the application info details activity
591        Intent baseIntent = event.task.key.baseIntent;
592        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
593                Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
594        intent.setComponent(intent.resolveActivity(getPackageManager()));
595        TaskStackBuilder.create(this)
596                .addNextIntentWithParentStack(intent).startActivities(null,
597                new UserHandle(event.task.key.userId));
598
599        // Keep track of app-info invocations
600        MetricsLogger.count(this, "overview_app_info", 1);
601    }
602
603    public final void onBusEvent(DismissTaskEvent event) {
604        // Remove any stored data from the loader
605        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
606        loader.deleteTaskData(event.task, false);
607
608        // Remove the task from activity manager
609        loader.getSystemServicesProxy().removeTask(event.task.key.id);
610    }
611
612    public final void onBusEvent(ResizeTaskEvent event) {
613        getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
614    }
615
616    public final void onBusEvent(DragStartEvent event) {
617        // Lock the orientation while dragging
618        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
619
620        // TODO: docking requires custom accessibility actions
621    }
622
623    public final void onBusEvent(DragEndEvent event) {
624        // Unlock the orientation when dragging completes
625        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
626    }
627
628    private void refreshSearchWidgetView() {
629        if (mSearchWidgetInfo != null) {
630            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
631            int searchWidgetId = ssp.getSearchAppWidgetId(this);
632            mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
633                    this, searchWidgetId, mSearchWidgetInfo);
634            Bundle opts = new Bundle();
635            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
636                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
637            mSearchWidgetHostView.updateAppWidgetOptions(opts);
638            // Set the padding to 0 for this search widget
639            mSearchWidgetHostView.setPadding(0, 0, 0, 0);
640            mRecentsView.setSearchBar(mSearchWidgetHostView);
641        } else {
642            mRecentsView.setSearchBar(null);
643        }
644    }
645}
646