RecentsActivity.java revision 1e8d71b605b4872e93200706a80a88a3ff25498c
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.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.os.Bundle;
30import android.util.Pair;
31import android.view.KeyEvent;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.WindowManager;
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.RecentsView;
40
41import java.util.ArrayList;
42import java.util.Set;
43
44/** Our special app widget host */
45class RecentsAppWidgetHost extends AppWidgetHost {
46    /* Callbacks to notify when an app package changes */
47    interface RecentsAppWidgetHostCallbacks {
48        public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo);
49    }
50
51    RecentsAppWidgetHostCallbacks mCb;
52
53    public RecentsAppWidgetHost(Context context, int hostId, RecentsAppWidgetHostCallbacks cb) {
54        super(context, hostId);
55        mCb = cb;
56    }
57
58    @Override
59    protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
60        mCb.onProviderChanged(appWidgetId, appWidget);
61    }
62}
63
64/* Activity */
65public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
66        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
67    FrameLayout mContainerView;
68    RecentsView mRecentsView;
69    View mEmptyView;
70
71    AppWidgetHost mAppWidgetHost;
72    AppWidgetProviderInfo mSearchAppWidgetInfo;
73    AppWidgetHostView mSearchAppWidgetHostView;
74
75    boolean mVisible;
76    boolean mTaskLaunched;
77
78    // Broadcast receiver to handle messages from our RecentsService
79    BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
80        @Override
81        public void onReceive(Context context, Intent intent) {
82            String action = intent.getAction();
83            Console.log(Constants.Log.App.SystemUIHandshake,
84                    "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
85            if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
86                // Dismiss recents, launching the focused task
87                dismissRecentsIfVisible();
88            } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
89                // Try and unfilter and filtered stacks
90                if (!mRecentsView.unfilterFilteredStacks()) {
91                    // If there are no filtered stacks, dismiss recents and launch the first task
92                    dismissRecentsIfVisible();
93                }
94            }
95        }
96    };
97
98    // Broadcast receiver to handle messages from the system
99    BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
100        @Override
101        public void onReceive(Context context, Intent intent) {
102            finish();
103        }
104    };
105
106    /** Updates the set of recent tasks */
107    void updateRecentsTasks(Intent launchIntent) {
108        // Update the configuration based on the launch intent
109        RecentsConfiguration config = RecentsConfiguration.getInstance();
110        config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra(
111                AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false);
112        config.launchedFromAltTab = launchIntent.getBooleanExtra(
113                AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false);
114
115        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
116        SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
117        ArrayList<TaskStack> stacks = root.getStacks();
118        if (!stacks.isEmpty()) {
119            mRecentsView.setBSP(root);
120        }
121
122        // Add the default no-recents layout
123        if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
124            mEmptyView.setVisibility(View.VISIBLE);
125
126            // Dim the background even more
127            WindowManager.LayoutParams wlp = getWindow().getAttributes();
128            wlp.dimAmount = Constants.Values.Window.DarkBackgroundDim;
129            getWindow().setAttributes(wlp);
130            getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
131        } else {
132            mEmptyView.setVisibility(View.GONE);
133
134            // Un-dim the background
135            WindowManager.LayoutParams wlp = getWindow().getAttributes();
136            wlp.dimAmount = 0f;
137            getWindow().setAttributes(wlp);
138            getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
139        }
140    }
141
142    /** Attempts to allocate and bind the search bar app widget */
143    void bindSearchBarAppWidget() {
144        if (Constants.DebugFlags.App.EnableSearchLayout) {
145            RecentsConfiguration config = RecentsConfiguration.getInstance();
146            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
147
148            // Reset the host view and widget info
149            mSearchAppWidgetHostView = null;
150            mSearchAppWidgetInfo = null;
151
152            // Try and load the app widget id from the settings
153            int appWidgetId = config.searchBarAppWidgetId;
154            if (appWidgetId >= 0) {
155                mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
156                if (mSearchAppWidgetInfo == null) {
157                    // If there is no actual widget associated with that id, then delete it and
158                    // prepare to bind another app widget in its place
159                    ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
160                    appWidgetId = -1;
161                }
162                Console.log(Constants.Log.App.SystemUIHandshake,
163                        "[RecentsActivity|onCreate|settings|appWidgetId]",
164                        "Id: " + appWidgetId,
165                        Console.AnsiBlue);
166            }
167
168            // If there is no id, then bind a new search app widget
169            if (appWidgetId < 0) {
170                Pair<Integer, AppWidgetProviderInfo> widgetInfo =
171                        ssp.bindSearchAppWidget(mAppWidgetHost);
172                if (widgetInfo != null) {
173                    Console.log(Constants.Log.App.SystemUIHandshake,
174                            "[RecentsActivity|onCreate|searchWidget]",
175                            "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
176                            Console.AnsiBlue);
177
178                    // Save the app widget id into the settings
179                    config.updateSearchBarAppWidgetId(this, widgetInfo.first);
180                    mSearchAppWidgetInfo = widgetInfo.second;
181                }
182            }
183        }
184    }
185
186    /** Creates the search bar app widget view */
187    void addSearchBarAppWidgetView() {
188        if (Constants.DebugFlags.App.EnableSearchLayout) {
189            RecentsConfiguration config = RecentsConfiguration.getInstance();
190            int appWidgetId = config.searchBarAppWidgetId;
191            if (appWidgetId >= 0) {
192                Console.log(Constants.Log.App.SystemUIHandshake,
193                "[RecentsActivity|onCreate|addSearchAppWidgetView]",
194                        "Id: " + appWidgetId,
195                        Console.AnsiBlue);
196                mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
197                        mSearchAppWidgetInfo);
198                Bundle opts = new Bundle();
199                opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
200                        AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS);
201                mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
202                // Set the padding to 0 for this search widget
203                mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
204                mRecentsView.setSearchBar(mSearchAppWidgetHostView);
205            } else {
206                mRecentsView.setSearchBar(null);
207            }
208        }
209    }
210
211    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
212    boolean dismissRecentsIfVisible() {
213        if (mVisible) {
214            if (!mRecentsView.launchFocusedTask()) {
215                if (!mRecentsView.launchFirstTask()) {
216                    finish();
217                }
218            }
219            return true;
220        }
221        return false;
222    }
223
224    /** Called with the activity is first created. */
225    @Override
226    public void onCreate(Bundle savedInstanceState) {
227        super.onCreate(savedInstanceState);
228        Console.logDivider(Constants.Log.App.SystemUIHandshake);
229        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
230                getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
231        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
232                Constants.Log.App.TimeRecentsStartupKey, "onCreate");
233
234        // Initialize the loader and the configuration
235        RecentsTaskLoader.initialize(this);
236        RecentsConfiguration.reinitialize(this);
237
238        // Initialize the widget host (the host id is static and does not change)
239        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
240
241        // Create the view hierarchy
242        mRecentsView = new RecentsView(this);
243        mRecentsView.setCallbacks(this);
244        mRecentsView.setLayoutParams(new FrameLayout.LayoutParams(
245                FrameLayout.LayoutParams.MATCH_PARENT,
246                FrameLayout.LayoutParams.MATCH_PARENT));
247
248        // Create the empty view
249        LayoutInflater inflater = LayoutInflater.from(this);
250        mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
251
252        mContainerView = new FrameLayout(this);
253        mContainerView.addView(mRecentsView);
254        mContainerView.addView(mEmptyView);
255        setContentView(mContainerView);
256
257        // Update the recent tasks
258        updateRecentsTasks(getIntent());
259
260        // Bind the search app widget when we first start up
261        bindSearchBarAppWidget();
262        // Add the search bar layout
263        addSearchBarAppWidgetView();
264    }
265
266    @Override
267    protected void onNewIntent(Intent intent) {
268        super.onNewIntent(intent);
269        // Reset the task launched flag if we encounter an onNewIntent() before onStop()
270        mTaskLaunched = false;
271
272        Console.logDivider(Constants.Log.App.SystemUIHandshake);
273        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
274                intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
275        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
276                Constants.Log.App.TimeRecentsStartupKey, "onNewIntent");
277
278        // Initialize the loader and the configuration
279        RecentsTaskLoader.initialize(this);
280        RecentsConfiguration.reinitialize(this);
281
282        // Update the recent tasks
283        updateRecentsTasks(intent);
284
285        // Don't attempt to rebind the search bar widget, but just add the search bar layout
286        addSearchBarAppWidgetView();
287    }
288
289    @Override
290    protected void onStart() {
291        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
292                Console.AnsiRed);
293        super.onStart();
294        mAppWidgetHost.startListening();
295        mVisible = true;
296    }
297
298    @Override
299    protected void onResume() {
300        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
301                Console.AnsiRed);
302        super.onResume();
303    }
304
305    @Override
306    public void onAttachedToWindow() {
307        Console.log(Constants.Log.App.SystemUIHandshake,
308                "[RecentsActivity|onAttachedToWindow]", "",
309                Console.AnsiRed);
310        super.onAttachedToWindow();
311
312        // Register the broadcast receiver to handle messages from our service
313        IntentFilter filter = new IntentFilter();
314        filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
315        filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
316        registerReceiver(mServiceBroadcastReceiver, filter);
317
318        // Register the broadcast receiver to handle messages when the screen is turned off
319        filter = new IntentFilter();
320        filter.addAction(Intent.ACTION_SCREEN_OFF);
321        registerReceiver(mScreenOffReceiver, filter);
322
323        // Register any broadcast receivers for the task loader
324        RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView);
325    }
326
327    @Override
328    public void onDetachedFromWindow() {
329        Console.log(Constants.Log.App.SystemUIHandshake,
330                "[RecentsActivity|onDetachedFromWindow]", "",
331                Console.AnsiRed);
332        super.onDetachedFromWindow();
333
334        // Unregister any broadcast receivers we have registered
335        unregisterReceiver(mServiceBroadcastReceiver);
336        unregisterReceiver(mScreenOffReceiver);
337        RecentsTaskLoader.getInstance().unregisterReceivers();
338    }
339
340    @Override
341    protected void onPause() {
342        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
343                Console.AnsiRed);
344        super.onPause();
345    }
346
347    @Override
348    protected void onStop() {
349        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
350                Console.AnsiRed);
351        super.onStop();
352
353        mAppWidgetHost.stopListening();
354        mVisible = false;
355        mTaskLaunched = false;
356    }
357
358    @Override
359    protected void onDestroy() {
360        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
361                Console.AnsiRed);
362        super.onDestroy();
363    }
364
365    @Override
366    public void onTrimMemory(int level) {
367        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
368        if (loader != null) {
369            loader.onTrimMemory(level);
370        }
371    }
372
373    @Override
374    public boolean onKeyDown(int keyCode, KeyEvent event) {
375        if (keyCode == KeyEvent.KEYCODE_TAB) {
376            // Focus the next task in the stack
377            final boolean backward = event.isShiftPressed();
378            mRecentsView.focusNextTask(!backward);
379            return true;
380        }
381
382        return super.onKeyDown(keyCode, event);
383    }
384
385    @Override
386    public void onBackPressed() {
387        boolean interceptedByInfoPanelClose = false;
388
389        // Try and return from any open info panes
390        if (Constants.DebugFlags.App.EnableInfoPane) {
391            interceptedByInfoPanelClose = mRecentsView.closeOpenInfoPanes();
392        }
393
394        // If we haven't been intercepted already, then unfilter any stacks
395        if (!interceptedByInfoPanelClose) {
396            if (!mRecentsView.unfilterFilteredStacks()) {
397                super.onBackPressed();
398            }
399        }
400    }
401
402    @Override
403    public void onTaskLaunching() {
404        mTaskLaunched = true;
405    }
406
407    @Override
408    public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
409        RecentsConfiguration config = RecentsConfiguration.getInstance();
410        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
411        if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) {
412            // The search provider may have changed, so just delete the old widget and bind it again
413            ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
414            config.updateSearchBarAppWidgetId(this, -1);
415            // Load the widget again
416            bindSearchBarAppWidget();
417            addSearchBarAppWidgetView();
418        }
419    }
420}
421