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