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