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