RecentsActivity.java revision 56e09b42a0f1670970872bef611a8036904ad6bf
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.appwidget.AppWidgetHostView; 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.SharedPreferences; 30import android.os.Bundle; 31import android.os.UserHandle; 32import android.util.Pair; 33import android.view.KeyEvent; 34import android.view.View; 35import android.view.ViewStub; 36import android.widget.Toast; 37import com.android.systemui.R; 38import com.android.systemui.recents.misc.Console; 39import com.android.systemui.recents.misc.DebugTrigger; 40import com.android.systemui.recents.misc.ReferenceCountedTrigger; 41import com.android.systemui.recents.misc.SystemServicesProxy; 42import com.android.systemui.recents.misc.Utilities; 43import com.android.systemui.recents.model.RecentsTaskLoader; 44import com.android.systemui.recents.model.SpaceNode; 45import com.android.systemui.recents.model.TaskStack; 46import com.android.systemui.recents.views.FullscreenTransitionOverlayView; 47import com.android.systemui.recents.views.RecentsView; 48import com.android.systemui.recents.views.SystemBarScrimViews; 49import com.android.systemui.recents.views.ViewAnimation; 50 51import java.lang.ref.WeakReference; 52import java.lang.reflect.InvocationTargetException; 53import java.util.ArrayList; 54 55/** 56 * The main Recents activity that is started from AlternateRecentsComponent. 57 */ 58public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, 59 RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks, 60 FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks { 61 62 // Actions and Extras sent from AlternateRecentsComponent 63 final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab"; 64 final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation"; 65 final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity"; 66 final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity"; 67 68 RecentsConfiguration mConfig; 69 boolean mVisible; 70 71 // Top level views 72 RecentsView mRecentsView; 73 SystemBarScrimViews mScrimViews; 74 ViewStub mEmptyViewStub; 75 View mEmptyView; 76 ViewStub mFullscreenOverlayStub; 77 FullscreenTransitionOverlayView mFullScreenOverlayView; 78 79 // Search AppWidget 80 RecentsAppWidgetHost mAppWidgetHost; 81 AppWidgetProviderInfo mSearchAppWidgetInfo; 82 AppWidgetHostView mSearchAppWidgetHostView; 83 84 85 // Runnables to finish the Recents activity 86 FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(); 87 FinishRecentsRunnable mFinishLaunchHomeRunnable; 88 89 /** 90 * A common Runnable to finish Recents either by calling finish() (with a custom animation) or 91 * launching Home with some ActivityOptions. Generally we always launch home when we exit 92 * Recents rather than just finishing the activity since we don't know what is behind Recents in 93 * the task stack. The only case where we finish() directly is when we are cancelling the full 94 * screen transition from the app. 95 */ 96 class FinishRecentsRunnable implements Runnable { 97 Intent mLaunchIntent; 98 ActivityOptions mLaunchOpts; 99 100 public FinishRecentsRunnable() { 101 // Do nothing 102 } 103 104 /** 105 * Creates a finish runnable that starts the specified intent, using the given 106 * ActivityOptions. 107 */ 108 public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) { 109 mLaunchIntent = launchIntent; 110 mLaunchOpts = opts; 111 } 112 113 @Override 114 public void run() { 115 // Mark Recents as no longer visible 116 AlternateRecentsComponent.notifyVisibilityChanged(false); 117 mVisible = false; 118 // Finish Recents 119 if (mLaunchIntent != null) { 120 if (mLaunchOpts != null) { 121 startActivityAsUser(mLaunchIntent, UserHandle.CURRENT); 122 } else { 123 startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); 124 } 125 } else { 126 finish(); 127 overridePendingTransition(R.anim.recents_to_launcher_enter, 128 R.anim.recents_to_launcher_exit); 129 } 130 } 131 } 132 133 /** 134 * Broadcast receiver to handle messages from AlternateRecentsComponent. 135 */ 136 final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { 137 @Override 138 public void onReceive(Context context, Intent intent) { 139 String action = intent.getAction(); 140 if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) { 141 if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) { 142 // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app 143 dismissRecentsToFocusedTaskOrHome(false); 144 } else { 145 // Otherwise, dismiss Recents to Home 146 dismissRecentsToHome(true); 147 } 148 } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) { 149 // If we are toggling Recents, then first unfilter any filtered stacks first 150 dismissRecentsToFocusedTaskOrHome(true); 151 } else if (action.equals(ACTION_START_ENTER_ANIMATION)) { 152 // Try and start the enter animation (or restart it on configuration changed) 153 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null); 154 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( 155 mFullScreenOverlayView, t)); 156 onEnterAnimationTriggered(); 157 } 158 } 159 }; 160 161 /** 162 * Broadcast receiver to handle messages from the system 163 */ 164 final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { 165 @Override 166 public void onReceive(Context context, Intent intent) { 167 String action = intent.getAction(); 168 if (action.equals(Intent.ACTION_SCREEN_OFF)) { 169 // When the screen turns off, dismiss Recents to Home 170 dismissRecentsToHome(false); 171 } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { 172 // When the search activity changes, update the Search widget 173 refreshSearchWidget(); 174 } 175 } 176 }; 177 178 /** 179 * A custom debug trigger to listen for a debug key chord. 180 */ 181 final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() { 182 @Override 183 public void run() { 184 onDebugModeTriggered(); 185 } 186 }); 187 188 /** Updates the set of recent tasks */ 189 void updateRecentsTasks(Intent launchIntent) { 190 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 191 SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount); 192 ArrayList<TaskStack> stacks = root.getStacks(); 193 if (!stacks.isEmpty()) { 194 mRecentsView.setBSP(root); 195 } 196 197 // Update the configuration based on the launch intent 198 mConfig.launchedFromHome = launchIntent.getBooleanExtra( 199 AlternateRecentsComponent.EXTRA_FROM_HOME, false); 200 mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra( 201 AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false); 202 mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra( 203 AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false); 204 mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( 205 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); 206 mConfig.launchedWithNoRecentTasks = !root.hasTasks(); 207 mConfig.launchedToTaskId = launchIntent.getIntExtra( 208 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1); 209 210 // Update the top level view's visibilities 211 if (mConfig.launchedWithNoRecentTasks) { 212 if (mEmptyView == null) { 213 mEmptyView = mEmptyViewStub.inflate(); 214 } 215 mEmptyView.setVisibility(View.VISIBLE); 216 mRecentsView.setSearchBarVisibility(View.GONE); 217 } else { 218 if (mEmptyView != null) { 219 mEmptyView.setVisibility(View.GONE); 220 } 221 if (mRecentsView.hasSearchBar()) { 222 mRecentsView.setSearchBarVisibility(View.VISIBLE); 223 } else { 224 addSearchBarAppWidgetView(); 225 } 226 } 227 228 // Animate the SystemUI scrims into view 229 mScrimViews.prepareEnterRecentsAnimation(); 230 } 231 232 /** Attempts to allocate and bind the search bar app widget */ 233 void bindSearchBarAppWidget() { 234 if (Constants.DebugFlags.App.EnableSearchLayout) { 235 SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); 236 237 // Reset the host view and widget info 238 mSearchAppWidgetHostView = null; 239 mSearchAppWidgetInfo = null; 240 241 // Try and load the app widget id from the settings 242 int appWidgetId = mConfig.searchBarAppWidgetId; 243 if (appWidgetId >= 0) { 244 mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId); 245 if (mSearchAppWidgetInfo == null) { 246 // If there is no actual widget associated with that id, then delete it and 247 // prepare to bind another app widget in its place 248 ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId); 249 appWidgetId = -1; 250 } 251 } 252 253 // If there is no id, then bind a new search app widget 254 if (appWidgetId < 0) { 255 Pair<Integer, AppWidgetProviderInfo> widgetInfo = 256 ssp.bindSearchAppWidget(mAppWidgetHost); 257 if (widgetInfo != null) { 258 // Save the app widget id into the settings 259 mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first); 260 mSearchAppWidgetInfo = widgetInfo.second; 261 } 262 } 263 } 264 } 265 266 /** Creates the search bar app widget view */ 267 void addSearchBarAppWidgetView() { 268 if (Constants.DebugFlags.App.EnableSearchLayout) { 269 int appWidgetId = mConfig.searchBarAppWidgetId; 270 if (appWidgetId >= 0) { 271 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId, 272 mSearchAppWidgetInfo); 273 Bundle opts = new Bundle(); 274 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 275 AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS); 276 mSearchAppWidgetHostView.updateAppWidgetOptions(opts); 277 // Set the padding to 0 for this search widget 278 mSearchAppWidgetHostView.setPadding(0, 0, 0, 0); 279 mRecentsView.setSearchBar(mSearchAppWidgetHostView); 280 } else { 281 mRecentsView.setSearchBar(null); 282 } 283 } 284 } 285 286 /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ 287 boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) { 288 if (mVisible) { 289 // If we are mid-animation into Recents, reverse the animation now 290 if (mFullScreenOverlayView != null && 291 mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true; 292 // If we currently have filtered stacks, then unfilter those first 293 if (checkFilteredStackState && 294 mRecentsView.unfilterFilteredStacks()) return true; 295 // If we have a focused Task, launch that Task now 296 if (mRecentsView.launchFocusedTask()) return true; 297 // If we launched from Home, then return to Home 298 if (mConfig.launchedFromHome) { 299 dismissRecentsToHomeRaw(true); 300 return true; 301 } 302 // Otherwise, try and return to the first Task in the stack 303 if (mRecentsView.launchFirstTask()) return true; 304 // If none of the other cases apply, then just go Home 305 dismissRecentsToHomeRaw(true); 306 return true; 307 } 308 return false; 309 } 310 311 /** Dismisses Recents directly to Home. */ 312 void dismissRecentsToHomeRaw(boolean animated) { 313 if (animated) { 314 ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, 315 null, mFinishLaunchHomeRunnable, null); 316 mRecentsView.startExitToHomeAnimation( 317 new ViewAnimation.TaskViewExitContext(exitTrigger)); 318 } else { 319 mFinishLaunchHomeRunnable.run(); 320 } 321 } 322 323 /** Dismisses Recents directly to Home if we currently aren't transitioning. */ 324 boolean dismissRecentsToHome(boolean animated) { 325 if (mVisible) { 326 // If we are mid-animation into Recents, reverse the animation now 327 if (mFullScreenOverlayView != null && 328 mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true; 329 // Return to Home 330 dismissRecentsToHomeRaw(animated); 331 return true; 332 } 333 return false; 334 } 335 336 /** Called with the activity is first created. */ 337 @Override 338 public void onCreate(Bundle savedInstanceState) { 339 super.onCreate(savedInstanceState); 340 341 // Initialize the loader and the configuration 342 RecentsTaskLoader.initialize(this); 343 mConfig = RecentsConfiguration.reinitialize(this, 344 RecentsTaskLoader.getInstance().getSystemServicesProxy()); 345 346 // Create the home intent runnable 347 Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); 348 homeIntent.addCategory(Intent.CATEGORY_HOME); 349 homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 350 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 351 mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, 352 ActivityOptions.makeCustomAnimation(this, R.anim.recents_to_launcher_enter, 353 R.anim.recents_to_launcher_exit)); 354 355 // Initialize the widget host (the host id is static and does not change) 356 mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); 357 358 // Set the Recents layout 359 setContentView(R.layout.recents); 360 mRecentsView = (RecentsView) findViewById(R.id.recents_view); 361 mRecentsView.setCallbacks(this); 362 mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 363 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 364 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 365 mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); 366 mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub); 367 mScrimViews = new SystemBarScrimViews(this, mConfig); 368 369 // Bind the search app widget when we first start up 370 bindSearchBarAppWidget(); 371 // Update the recent tasks 372 updateRecentsTasks(getIntent()); 373 374 // Register the broadcast receiver to handle messages when the screen is turned off 375 IntentFilter filter = new IntentFilter(); 376 filter.addAction(Intent.ACTION_SCREEN_OFF); 377 filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 378 registerReceiver(mSystemBroadcastReceiver, filter); 379 380 // Register any broadcast receivers for the task loader 381 RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView); 382 383 // Private API calls to make the shadows look better 384 try { 385 Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f)); 386 Utilities.setShadowProperty("ambientRatio", String.valueOf(0.5f)); 387 } catch (IllegalAccessException e) { 388 e.printStackTrace(); 389 } catch (InvocationTargetException e) { 390 e.printStackTrace(); 391 } 392 393 // Prepare the screenshot transition if necessary 394 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 395 mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate(); 396 mFullScreenOverlayView.setCallbacks(this); 397 mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); 398 } 399 400 // Update if we are getting a configuration change 401 if (savedInstanceState != null) { 402 mConfig.updateOnConfigurationChange(); 403 onConfigurationChange(); 404 } 405 } 406 407 void onConfigurationChange() { 408 // Update RecentsConfiguration 409 mConfig = RecentsConfiguration.reinitialize(this, 410 RecentsTaskLoader.getInstance().getSystemServicesProxy()); 411 412 // Try and start the enter animation (or restart it on configuration changed) 413 ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); 414 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( 415 mFullScreenOverlayView, t)); 416 onEnterAnimationTriggered(); 417 } 418 419 @Override 420 protected void onNewIntent(Intent intent) { 421 super.onNewIntent(intent); 422 setIntent(intent); 423 424 // Update the recent tasks 425 updateRecentsTasks(intent); 426 427 // Prepare the screenshot transition if necessary 428 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 429 mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); 430 } 431 } 432 433 @Override 434 protected void onStart() { 435 super.onStart(); 436 437 // Register the broadcast receiver to handle messages from our service 438 IntentFilter filter = new IntentFilter(); 439 filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY); 440 filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY); 441 filter.addAction(ACTION_START_ENTER_ANIMATION); 442 registerReceiver(mServiceBroadcastReceiver, filter); 443 } 444 445 @Override 446 protected void onResume() { 447 super.onResume(); 448 449 // Start listening for widget package changes if there is one bound, post it since we don't 450 // want it stalling the startup 451 if (mConfig.searchBarAppWidgetId >= 0) { 452 final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> callback = 453 new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(this); 454 mRecentsView.postDelayed(new Runnable() { 455 @Override 456 public void run() { 457 RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = callback.get(); 458 if (cb != null) { 459 mAppWidgetHost.startListening(cb); 460 } 461 } 462 }, 1); 463 } 464 465 // Mark Recents as visible 466 mVisible = true; 467 } 468 469 @Override 470 protected void onStop() { 471 super.onStop(); 472 473 // Unregister the RecentsService receiver 474 unregisterReceiver(mServiceBroadcastReceiver); 475 476 // Stop listening for widget package changes if there was one bound 477 if (mAppWidgetHost.isListening()) { 478 mAppWidgetHost.stopListening(); 479 } 480 } 481 482 @Override 483 protected void onDestroy() { 484 super.onDestroy(); 485 486 // Unregister the system broadcast receivers 487 unregisterReceiver(mSystemBroadcastReceiver); 488 RecentsTaskLoader.getInstance().unregisterReceivers(); 489 } 490 491 @Override 492 public void onTrimMemory(int level) { 493 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 494 if (loader != null) { 495 loader.onTrimMemory(level); 496 } 497 } 498 499 @Override 500 public boolean onKeyDown(int keyCode, KeyEvent event) { 501 if (keyCode == KeyEvent.KEYCODE_TAB) { 502 // Focus the next task in the stack 503 final boolean backward = event.isShiftPressed(); 504 mRecentsView.focusNextTask(!backward); 505 return true; 506 } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { 507 mRecentsView.focusNextTask(true); 508 return true; 509 } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 510 mRecentsView.focusNextTask(false); 511 return true; 512 } 513 // Pass through the debug trigger 514 mDebugTrigger.onKeyEvent(keyCode); 515 return super.onKeyDown(keyCode, event); 516 } 517 518 @Override 519 public void onUserInteraction() { 520 mRecentsView.onUserInteraction(); 521 } 522 523 @Override 524 public void onBackPressed() { 525 // Test mode where back does not do anything 526 if (mConfig.debugModeEnabled) return; 527 528 // Dismiss Recents to the focused Task or Home 529 dismissRecentsToFocusedTaskOrHome(true); 530 } 531 532 /** Called when debug mode is triggered */ 533 public void onDebugModeTriggered() { 534 if (mConfig.developerOptionsEnabled) { 535 SharedPreferences settings = getSharedPreferences(getPackageName(), 0); 536 if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) { 537 // Disable the debug mode 538 settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply(); 539 } else { 540 // Enable the debug mode 541 settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply(); 542 } 543 Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + 544 ") toggled, please restart Recents now", Toast.LENGTH_SHORT).show(); 545 } 546 } 547 548 /** Called when the enter recents animation is triggered. */ 549 public void onEnterAnimationTriggered() { 550 // Animate the SystemUI scrim views 551 mScrimViews.startEnterRecentsAnimation(); 552 } 553 554 /**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/ 555 556 @Override 557 public void onEnterAnimationComplete() { 558 // Reset the full screenshot transition view 559 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 560 mFullScreenOverlayView.reset(); 561 562 // Recycle the full screen screenshot 563 AlternateRecentsComponent.consumeLastScreenshot(); 564 } 565 } 566 567 /**** RecentsView.RecentsViewCallbacks Implementation ****/ 568 569 @Override 570 public void onExitToHomeAnimationTriggered() { 571 // Animate the SystemUI scrim views out 572 mScrimViews.startExitRecentsAnimation(); 573 } 574 575 @Override 576 public void onTaskViewClicked() { 577 // Mark recents as no longer visible 578 AlternateRecentsComponent.notifyVisibilityChanged(false); 579 mVisible = false; 580 } 581 582 @Override 583 public void onAllTaskViewsDismissed() { 584 mFinishLaunchHomeRunnable.run(); 585 } 586 587 /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/ 588 589 @Override 590 public void refreshSearchWidget() { 591 bindSearchBarAppWidget(); 592 addSearchBarAppWidgetView(); 593 } 594} 595