PhoneWindow.java revision d6107a3170df61d9e776fcd5666acfc9135c6f16
1/*
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package com.android.internal.policy.impl;
17
18import static android.view.View.MeasureSpec.AT_MOST;
19import static android.view.View.MeasureSpec.EXACTLY;
20import static android.view.View.MeasureSpec.getMode;
21import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
23import static android.view.WindowManager.LayoutParams.*;
24
25import android.animation.Animator;
26import android.animation.ObjectAnimator;
27import android.app.ActivityOptions;
28import android.os.Looper;
29import android.transition.Fade;
30import android.transition.Scene;
31import android.transition.Transition;
32import android.transition.TransitionInflater;
33import android.transition.TransitionManager;
34import android.transition.TransitionSet;
35import android.util.ArrayMap;
36import android.view.ViewConfiguration;
37
38import com.android.internal.R;
39import com.android.internal.view.RootViewSurfaceTaker;
40import com.android.internal.view.StandaloneActionMode;
41import com.android.internal.view.menu.ContextMenuBuilder;
42import com.android.internal.view.menu.IconMenuPresenter;
43import com.android.internal.view.menu.ListMenuPresenter;
44import com.android.internal.view.menu.MenuBuilder;
45import com.android.internal.view.menu.MenuDialogHelper;
46import com.android.internal.view.menu.MenuPresenter;
47import com.android.internal.view.menu.MenuView;
48import com.android.internal.widget.ActionBarContainer;
49import com.android.internal.widget.ActionBarContextView;
50import com.android.internal.widget.ActionBarOverlayLayout;
51import com.android.internal.widget.ActionBarView;
52import com.android.internal.widget.SwipeDismissLayout;
53
54import android.app.KeyguardManager;
55import android.content.Context;
56import android.content.pm.ActivityInfo;
57import android.content.res.Configuration;
58import android.content.res.Resources;
59import android.content.res.TypedArray;
60import android.graphics.Canvas;
61import android.graphics.PixelFormat;
62import android.graphics.Rect;
63import android.graphics.drawable.Drawable;
64import android.media.AudioManager;
65import android.net.Uri;
66import android.os.Bundle;
67import android.os.Handler;
68import android.os.Parcel;
69import android.os.Parcelable;
70import android.os.RemoteException;
71import android.os.ServiceManager;
72import android.util.AndroidRuntimeException;
73import android.util.DisplayMetrics;
74import android.util.EventLog;
75import android.util.Log;
76import android.util.SparseArray;
77import android.util.TypedValue;
78import android.view.ActionMode;
79import android.view.ContextThemeWrapper;
80import android.view.Gravity;
81import android.view.IRotationWatcher;
82import android.view.IWindowManager;
83import android.view.InputEvent;
84import android.view.InputQueue;
85import android.view.KeyCharacterMap;
86import android.view.KeyEvent;
87import android.view.LayoutInflater;
88import android.view.Menu;
89import android.view.MenuItem;
90import android.view.MotionEvent;
91import android.view.SurfaceHolder;
92import android.view.View;
93import android.view.ViewGroup;
94import android.view.ViewManager;
95import android.view.ViewParent;
96import android.view.ViewRootImpl;
97import android.view.ViewStub;
98import android.view.ViewTreeObserver;
99import android.view.Window;
100import android.view.WindowManager;
101import android.view.accessibility.AccessibilityEvent;
102import android.view.accessibility.AccessibilityManager;
103import android.view.animation.Animation;
104import android.view.animation.AnimationUtils;
105import android.widget.FrameLayout;
106import android.widget.ImageView;
107import android.widget.PopupWindow;
108import android.widget.ProgressBar;
109import android.widget.TextView;
110
111import java.lang.ref.WeakReference;
112import java.util.ArrayList;
113import java.util.Collection;
114import java.util.Map;
115
116/**
117 * Android-specific Window.
118 * <p>
119 * todo: need to pull the generic functionality out into a base class
120 * in android.widget.
121 */
122public class PhoneWindow extends Window implements MenuBuilder.Callback {
123
124    private final static String TAG = "PhoneWindow";
125
126    private final static boolean SWEEP_OPEN_MENU = false;
127    private static final long MAX_TRANSITION_START_WAIT = 500;
128    private static final long MAX_TRANSITION_FINISH_WAIT = 1000;
129
130    private static final String KEY_SCREEN_X = "shared_element:screenX";
131    private static final String KEY_SCREEN_Y = "shared_element:screenY";
132    private static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
133    private static final String KEY_WIDTH = "shared_element:width";
134    private static final String KEY_HEIGHT = "shared_element:height";
135    private static final String KEY_NAME = "shared_element:name";
136
137    /**
138     * Simple callback used by the context menu and its submenus. The options
139     * menu submenus do not use this (their behavior is more complex).
140     */
141    final DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
142
143    final TypedValue mMinWidthMajor = new TypedValue();
144    final TypedValue mMinWidthMinor = new TypedValue();
145    TypedValue mFixedWidthMajor;
146    TypedValue mFixedWidthMinor;
147    TypedValue mFixedHeightMajor;
148    TypedValue mFixedHeightMinor;
149    TypedValue mOutsetBottom;
150
151    // This is the top-level view of the window, containing the window decor.
152    private DecorView mDecor;
153
154    // This is the view in which the window contents are placed. It is either
155    // mDecor itself, or a child of mDecor where the contents go.
156    private ViewGroup mContentParent;
157
158    SurfaceHolder.Callback2 mTakeSurfaceCallback;
159
160    InputQueue.Callback mTakeInputQueueCallback;
161
162    private boolean mIsFloating;
163
164    private LayoutInflater mLayoutInflater;
165
166    private TextView mTitleView;
167
168    private ActionBarView mActionBar;
169    private ActionMenuPresenterCallback mActionMenuPresenterCallback;
170    private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
171
172    private TransitionManager mTransitionManager;
173    private Scene mContentScene;
174
175    // The icon resource has been explicitly set elsewhere
176    // and should not be overwritten with a default.
177    static final int FLAG_RESOURCE_SET_ICON = 1 << 0;
178
179    // The logo resource has been explicitly set elsewhere
180    // and should not be overwritten with a default.
181    static final int FLAG_RESOURCE_SET_LOGO = 1 << 1;
182
183    // The icon resource is currently configured to use the system fallback
184    // as no default was previously specified. Anything can override this.
185    static final int FLAG_RESOURCE_SET_ICON_FALLBACK = 1 << 2;
186
187    int mResourcesSetFlags;
188    int mIconRes;
189    int mLogoRes;
190
191    private DrawableFeatureState[] mDrawables;
192
193    private PanelFeatureState[] mPanels;
194
195    /**
196     * The panel that is prepared or opened (the most recent one if there are
197     * multiple panels). Shortcuts will go to this panel. It gets set in
198     * {@link #preparePanel} and cleared in {@link #closePanel}.
199     */
200    private PanelFeatureState mPreparedPanel;
201
202    /**
203     * The keycode that is currently held down (as a modifier) for chording. If
204     * this is 0, there is no key held down.
205     */
206    private int mPanelChordingKey;
207
208    private ImageView mLeftIconView;
209
210    private ImageView mRightIconView;
211
212    private ProgressBar mCircularProgressBar;
213
214    private ProgressBar mHorizontalProgressBar;
215
216    private int mBackgroundResource = 0;
217
218    private Drawable mBackgroundDrawable;
219
220    private int mFrameResource = 0;
221
222    private int mTextColor = 0;
223
224    private CharSequence mTitle = null;
225
226    private int mTitleColor = 0;
227
228    private boolean mAlwaysReadCloseOnTouchAttr = false;
229
230    private ContextMenuBuilder mContextMenu;
231    private MenuDialogHelper mContextMenuHelper;
232    private boolean mClosingActionMenu;
233
234    private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
235
236    private AudioManager mAudioManager;
237    private KeyguardManager mKeyguardManager;
238
239    private int mUiOptions = 0;
240
241    private boolean mInvalidatePanelMenuPosted;
242    private int mInvalidatePanelMenuFeatures;
243    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
244        @Override public void run() {
245            for (int i = 0; i <= FEATURE_MAX; i++) {
246                if ((mInvalidatePanelMenuFeatures & 1 << i) != 0) {
247                    doInvalidatePanelMenu(i);
248                }
249            }
250            mInvalidatePanelMenuPosted = false;
251            mInvalidatePanelMenuFeatures = 0;
252        }
253    };
254
255    private ActivityOptions mActivityOptions;
256    private SceneTransitionListener mSceneTransitionListener;
257    private boolean mAllowEnterOverlap = true;
258    private boolean mAllowExitOverlap = true;
259    private Map<String, String> mSharedElementsMap;
260    private ArrayList<View> mTransitioningViews;
261
262    static class WindowManagerHolder {
263        static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
264                ServiceManager.getService("window"));
265    }
266
267    static final RotationWatcher sRotationWatcher = new RotationWatcher();
268
269    public PhoneWindow(Context context) {
270        super(context);
271        mLayoutInflater = LayoutInflater.from(context);
272    }
273
274    @Override
275    public final void setContainer(Window container) {
276        super.setContainer(container);
277    }
278
279    @Override
280    public boolean requestFeature(int featureId) {
281        if (mContentParent != null) {
282            throw new AndroidRuntimeException("requestFeature() must be called before adding content");
283        }
284        final int features = getFeatures();
285        if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) {
286
287            /* Another feature is enabled and the user is trying to enable the custom title feature */
288            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
289        }
290        if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) &&
291                (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) {
292
293            /* Custom title feature is enabled and the user is trying to enable another feature */
294            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
295        }
296        if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
297            return false; // Ignore. No title dominates.
298        }
299        if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {
300            // Remove the action bar feature if we have no title. No title dominates.
301            removeFeature(FEATURE_ACTION_BAR);
302        }
303
304        if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_SWIPE_TO_DISMISS) {
305            throw new AndroidRuntimeException(
306                    "You cannot combine swipe dismissal and the action bar.");
307        }
308        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0 && featureId == FEATURE_ACTION_BAR) {
309            throw new AndroidRuntimeException(
310                    "You cannot combine swipe dismissal and the action bar.");
311        }
312        return super.requestFeature(featureId);
313    }
314
315    @Override
316    public void setUiOptions(int uiOptions) {
317        mUiOptions = uiOptions;
318    }
319
320    @Override
321    public void setUiOptions(int uiOptions, int mask) {
322        mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);
323    }
324
325    @Override
326    public TransitionManager getTransitionManager() {
327        return mTransitionManager;
328    }
329
330    @Override
331    public void setTransitionManager(TransitionManager tm) {
332        mTransitionManager = tm;
333    }
334
335    @Override
336    public Scene getContentScene() {
337        return mContentScene;
338    }
339
340    @Override
341    public void setContentView(int layoutResID) {
342        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
343        // decor, when theme attributes and the like are crystalized. Do not check the feature
344        // before this happens.
345        if (mContentParent == null) {
346            installDecor();
347        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
348            mContentParent.removeAllViews();
349        }
350
351        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
352            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
353                    getContext());
354            transitionTo(newScene);
355        } else {
356            mLayoutInflater.inflate(layoutResID, mContentParent);
357        }
358        final Callback cb = getCallback();
359        if (cb != null && !isDestroyed()) {
360            cb.onContentChanged();
361        }
362    }
363
364    @Override
365    public void setContentView(View view) {
366        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
367    }
368
369    @Override
370    public void setContentView(View view, ViewGroup.LayoutParams params) {
371        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
372        // decor, when theme attributes and the like are crystalized. Do not check the feature
373        // before this happens.
374        if (mContentParent == null) {
375            installDecor();
376        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
377            mContentParent.removeAllViews();
378        }
379
380        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
381            view.setLayoutParams(params);
382            final Scene newScene = new Scene(mContentParent, view);
383            transitionTo(newScene);
384        } else {
385            mContentParent.addView(view, params);
386        }
387        final Callback cb = getCallback();
388        if (cb != null && !isDestroyed()) {
389            cb.onContentChanged();
390        }
391    }
392
393    @Override
394    public void addContentView(View view, ViewGroup.LayoutParams params) {
395        if (mContentParent == null) {
396            installDecor();
397        }
398        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
399            // TODO Augment the scenes/transitions API to support this.
400            throw new UnsupportedOperationException(
401                    "addContentView does not support content transitions");
402        }
403        mContentParent.addView(view, params);
404        final Callback cb = getCallback();
405        if (cb != null && !isDestroyed()) {
406            cb.onContentChanged();
407        }
408    }
409
410    private void transitionTo(Scene scene) {
411        if (mContentScene == null) {
412            scene.enter();
413            if (mActivityOptions != null) {
414                new EnterScene().start();
415            }
416        } else {
417            mTransitionManager.transitionTo(scene);
418        }
419        mContentScene = scene;
420    }
421
422    @Override
423    public View getCurrentFocus() {
424        return mDecor != null ? mDecor.findFocus() : null;
425    }
426
427    @Override
428    public void takeSurface(SurfaceHolder.Callback2 callback) {
429        mTakeSurfaceCallback = callback;
430    }
431
432    public void takeInputQueue(InputQueue.Callback callback) {
433        mTakeInputQueueCallback = callback;
434    }
435
436    @Override
437    public boolean isFloating() {
438        return mIsFloating;
439    }
440
441    /**
442     * Return a LayoutInflater instance that can be used to inflate XML view layout
443     * resources for use in this Window.
444     *
445     * @return LayoutInflater The shared LayoutInflater.
446     */
447    @Override
448    public LayoutInflater getLayoutInflater() {
449        return mLayoutInflater;
450    }
451
452    @Override
453    public void setTitle(CharSequence title) {
454        if (mTitleView != null) {
455            mTitleView.setText(title);
456        } else if (mActionBar != null) {
457            mActionBar.setWindowTitle(title);
458        }
459        mTitle = title;
460    }
461
462    @Override
463    @Deprecated
464    public void setTitleColor(int textColor) {
465        if (mTitleView != null) {
466            mTitleView.setTextColor(textColor);
467        }
468        mTitleColor = textColor;
469    }
470
471    /**
472     * Prepares the panel to either be opened or chorded. This creates the Menu
473     * instance for the panel and populates it via the Activity callbacks.
474     *
475     * @param st The panel state to prepare.
476     * @param event The event that triggered the preparing of the panel.
477     * @return Whether the panel was prepared. If the panel should not be shown,
478     *         returns false.
479     */
480    public final boolean preparePanel(PanelFeatureState st, KeyEvent event) {
481        if (isDestroyed()) {
482            return false;
483        }
484
485        // Already prepared (isPrepared will be reset to false later)
486        if (st.isPrepared) {
487            return true;
488        }
489
490        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
491            // Another Panel is prepared and possibly open, so close it
492            closePanel(mPreparedPanel, false);
493        }
494
495        final Callback cb = getCallback();
496
497        if (cb != null) {
498            st.createdPanelView = cb.onCreatePanelView(st.featureId);
499        }
500
501        final boolean isActionBarMenu =
502                (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR);
503
504        if (isActionBarMenu && mActionBar != null) {
505            // Enforce ordering guarantees around events so that the action bar never
506            // dispatches menu-related events before the panel is prepared.
507            mActionBar.setMenuPrepared();
508        }
509
510        if (st.createdPanelView == null) {
511            // Init the panel state's menu--return false if init failed
512            if (st.menu == null || st.refreshMenuContent) {
513                if (st.menu == null) {
514                    if (!initializePanelMenu(st) || (st.menu == null)) {
515                        return false;
516                    }
517                }
518
519                if (isActionBarMenu && mActionBar != null) {
520                    if (mActionMenuPresenterCallback == null) {
521                        mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
522                    }
523                    mActionBar.setMenu(st.menu, mActionMenuPresenterCallback);
524                }
525
526                // Call callback, and return if it doesn't want to display menu.
527
528                // Creating the panel menu will involve a lot of manipulation;
529                // don't dispatch change events to presenters until we're done.
530                st.menu.stopDispatchingItemsChanged();
531                if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
532                    // Ditch the menu created above
533                    st.setMenu(null);
534
535                    if (isActionBarMenu && mActionBar != null) {
536                        // Don't show it in the action bar either
537                        mActionBar.setMenu(null, mActionMenuPresenterCallback);
538                    }
539
540                    return false;
541                }
542
543                st.refreshMenuContent = false;
544            }
545
546            // Callback and return if the callback does not want to show the menu
547
548            // Preparing the panel menu can involve a lot of manipulation;
549            // don't dispatch change events to presenters until we're done.
550            st.menu.stopDispatchingItemsChanged();
551
552            // Restore action view state before we prepare. This gives apps
553            // an opportunity to override frozen/restored state in onPrepare.
554            if (st.frozenActionViewState != null) {
555                st.menu.restoreActionViewStates(st.frozenActionViewState);
556                st.frozenActionViewState = null;
557            }
558
559            if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
560                if (isActionBarMenu && mActionBar != null) {
561                    // The app didn't want to show the menu for now but it still exists.
562                    // Clear it out of the action bar.
563                    mActionBar.setMenu(null, mActionMenuPresenterCallback);
564                }
565                st.menu.startDispatchingItemsChanged();
566                return false;
567            }
568
569            // Set the proper keymap
570            KeyCharacterMap kmap = KeyCharacterMap.load(
571                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
572            st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
573            st.menu.setQwertyMode(st.qwertyMode);
574            st.menu.startDispatchingItemsChanged();
575        }
576
577        // Set other state
578        st.isPrepared = true;
579        st.isHandled = false;
580        mPreparedPanel = st;
581
582        return true;
583    }
584
585    @Override
586    public void onConfigurationChanged(Configuration newConfig) {
587        // Action bars handle their own menu state
588        if (mActionBar == null) {
589            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
590            if ((st != null) && (st.menu != null)) {
591                if (st.isOpen) {
592                    // Freeze state
593                    final Bundle state = new Bundle();
594                    if (st.iconMenuPresenter != null) {
595                        st.iconMenuPresenter.saveHierarchyState(state);
596                    }
597                    if (st.listMenuPresenter != null) {
598                        st.listMenuPresenter.saveHierarchyState(state);
599                    }
600
601                    // Remove the menu views since they need to be recreated
602                    // according to the new configuration
603                    clearMenuViews(st);
604
605                    // Re-open the same menu
606                    reopenMenu(false);
607
608                    // Restore state
609                    if (st.iconMenuPresenter != null) {
610                        st.iconMenuPresenter.restoreHierarchyState(state);
611                    }
612                    if (st.listMenuPresenter != null) {
613                        st.listMenuPresenter.restoreHierarchyState(state);
614                    }
615
616                } else {
617                    // Clear menu views so on next menu opening, it will use
618                    // the proper layout
619                    clearMenuViews(st);
620                }
621            }
622        }
623    }
624
625    private static void clearMenuViews(PanelFeatureState st) {
626        // This can be called on config changes, so we should make sure
627        // the views will be reconstructed based on the new orientation, etc.
628
629        // Allow the callback to create a new panel view
630        st.createdPanelView = null;
631
632        // Causes the decor view to be recreated
633        st.refreshDecorView = true;
634
635        st.clearMenuPresenters();
636    }
637
638    @Override
639    public final void openPanel(int featureId, KeyEvent event) {
640        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
641                mActionBar.isOverflowReserved() &&
642                !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {
643            if (mActionBar.getVisibility() == View.VISIBLE) {
644                mActionBar.showOverflowMenu();
645            }
646        } else {
647            openPanel(getPanelState(featureId, true), event);
648        }
649    }
650
651    private void openPanel(final PanelFeatureState st, KeyEvent event) {
652        // System.out.println("Open panel: isOpen=" + st.isOpen);
653
654        // Already open, return
655        if (st.isOpen || isDestroyed()) {
656            return;
657        }
658
659        // Don't open an options panel for honeycomb apps on xlarge devices.
660        // (The app should be using an action bar for menu items.)
661        if (st.featureId == FEATURE_OPTIONS_PANEL) {
662            Context context = getContext();
663            Configuration config = context.getResources().getConfiguration();
664            boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==
665                    Configuration.SCREENLAYOUT_SIZE_XLARGE;
666            boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >=
667                    android.os.Build.VERSION_CODES.HONEYCOMB;
668
669            if (isXLarge && isHoneycombApp) {
670                return;
671            }
672        }
673
674        Callback cb = getCallback();
675        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
676            // Callback doesn't want the menu to open, reset any state
677            closePanel(st, true);
678            return;
679        }
680
681        final WindowManager wm = getWindowManager();
682        if (wm == null) {
683            return;
684        }
685
686        // Prepare panel (should have been done before, but just in case)
687        if (!preparePanel(st, event)) {
688            return;
689        }
690
691        int width = WRAP_CONTENT;
692        if (st.decorView == null || st.refreshDecorView) {
693            if (st.decorView == null) {
694                // Initialize the panel decor, this will populate st.decorView
695                if (!initializePanelDecor(st) || (st.decorView == null))
696                    return;
697            } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
698                // Decor needs refreshing, so remove its views
699                st.decorView.removeAllViews();
700            }
701
702            // This will populate st.shownPanelView
703            if (!initializePanelContent(st) || !st.hasPanelItems()) {
704                return;
705            }
706
707            ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
708            if (lp == null) {
709                lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
710            }
711
712            int backgroundResId;
713            if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
714                // If the contents is fill parent for the width, set the
715                // corresponding background
716                backgroundResId = st.fullBackground;
717                width = MATCH_PARENT;
718            } else {
719                // Otherwise, set the normal panel background
720                backgroundResId = st.background;
721            }
722            st.decorView.setWindowBackground(getContext().getDrawable(
723                    backgroundResId));
724
725            ViewParent shownPanelParent = st.shownPanelView.getParent();
726            if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {
727                ((ViewGroup) shownPanelParent).removeView(st.shownPanelView);
728            }
729            st.decorView.addView(st.shownPanelView, lp);
730
731            /*
732             * Give focus to the view, if it or one of its children does not
733             * already have it.
734             */
735            if (!st.shownPanelView.hasFocus()) {
736                st.shownPanelView.requestFocus();
737            }
738        } else if (!st.isInListMode()) {
739            width = MATCH_PARENT;
740        } else if (st.createdPanelView != null) {
741            // If we already had a panel view, carry width=MATCH_PARENT through
742            // as we did above when it was created.
743            ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams();
744            if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
745                width = MATCH_PARENT;
746            }
747        }
748
749        st.isHandled = false;
750
751        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
752                width, WRAP_CONTENT,
753                st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
754                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
755                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
756                st.decorView.mDefaultOpacity);
757
758        if (st.isCompact) {
759            lp.gravity = getOptionsPanelGravity();
760            sRotationWatcher.addWindow(this);
761        } else {
762            lp.gravity = st.gravity;
763        }
764
765        lp.windowAnimations = st.windowAnimations;
766
767        wm.addView(st.decorView, lp);
768        st.isOpen = true;
769        // Log.v(TAG, "Adding main menu to window manager.");
770    }
771
772    @Override
773    public final void closePanel(int featureId) {
774        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
775                mActionBar.isOverflowReserved() &&
776                !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {
777            mActionBar.hideOverflowMenu();
778        } else if (featureId == FEATURE_CONTEXT_MENU) {
779            closeContextMenu();
780        } else {
781            closePanel(getPanelState(featureId, true), true);
782        }
783    }
784
785    /**
786     * Closes the given panel.
787     *
788     * @param st The panel to be closed.
789     * @param doCallback Whether to notify the callback that the panel was
790     *            closed. If the panel is in the process of re-opening or
791     *            opening another panel (e.g., menu opening a sub menu), the
792     *            callback should not happen and this variable should be false.
793     *            In addition, this method internally will only perform the
794     *            callback if the panel is open.
795     */
796    public final void closePanel(PanelFeatureState st, boolean doCallback) {
797        // System.out.println("Close panel: isOpen=" + st.isOpen);
798        if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&
799                mActionBar != null && mActionBar.isOverflowMenuShowing()) {
800            checkCloseActionMenu(st.menu);
801            return;
802        }
803
804        final ViewManager wm = getWindowManager();
805        if ((wm != null) && st.isOpen) {
806            if (st.decorView != null) {
807                wm.removeView(st.decorView);
808                // Log.v(TAG, "Removing main menu from window manager.");
809                if (st.isCompact) {
810                    sRotationWatcher.removeWindow(this);
811                }
812            }
813
814            if (doCallback) {
815                callOnPanelClosed(st.featureId, st, null);
816            }
817        }
818
819        st.isPrepared = false;
820        st.isHandled = false;
821        st.isOpen = false;
822
823        // This view is no longer shown, so null it out
824        st.shownPanelView = null;
825
826        if (st.isInExpandedMode) {
827            // Next time the menu opens, it should not be in expanded mode, so
828            // force a refresh of the decor
829            st.refreshDecorView = true;
830            st.isInExpandedMode = false;
831        }
832
833        if (mPreparedPanel == st) {
834            mPreparedPanel = null;
835            mPanelChordingKey = 0;
836        }
837    }
838
839    void checkCloseActionMenu(Menu menu) {
840        if (mClosingActionMenu) {
841            return;
842        }
843
844        mClosingActionMenu = true;
845        mActionBar.dismissPopupMenus();
846        Callback cb = getCallback();
847        if (cb != null && !isDestroyed()) {
848            cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
849        }
850        mClosingActionMenu = false;
851    }
852
853    @Override
854    public final void togglePanel(int featureId, KeyEvent event) {
855        PanelFeatureState st = getPanelState(featureId, true);
856        if (st.isOpen) {
857            closePanel(st, true);
858        } else {
859            openPanel(st, event);
860        }
861    }
862
863    @Override
864    public void invalidatePanelMenu(int featureId) {
865        mInvalidatePanelMenuFeatures |= 1 << featureId;
866
867        if (!mInvalidatePanelMenuPosted && mDecor != null) {
868            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
869            mInvalidatePanelMenuPosted = true;
870        }
871    }
872
873    void doInvalidatePanelMenu(int featureId) {
874        PanelFeatureState st = getPanelState(featureId, true);
875        Bundle savedActionViewStates = null;
876        if (st.menu != null) {
877            savedActionViewStates = new Bundle();
878            st.menu.saveActionViewStates(savedActionViewStates);
879            if (savedActionViewStates.size() > 0) {
880                st.frozenActionViewState = savedActionViewStates;
881            }
882            // This will be started again when the panel is prepared.
883            st.menu.stopDispatchingItemsChanged();
884            st.menu.clear();
885        }
886        st.refreshMenuContent = true;
887        st.refreshDecorView = true;
888
889        // Prepare the options panel if we have an action bar
890        if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
891                && mActionBar != null) {
892            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
893            if (st != null) {
894                st.isPrepared = false;
895                preparePanel(st, null);
896            }
897        }
898    }
899
900    /**
901     * Called when the panel key is pushed down.
902     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
903     * @param event The key event.
904     * @return Whether the key was handled.
905     */
906    public final boolean onKeyDownPanel(int featureId, KeyEvent event) {
907        final int keyCode = event.getKeyCode();
908
909        if (event.getRepeatCount() == 0) {
910            // The panel key was pushed, so set the chording key
911            mPanelChordingKey = keyCode;
912
913            PanelFeatureState st = getPanelState(featureId, true);
914            if (!st.isOpen) {
915                return preparePanel(st, event);
916            }
917        }
918
919        return false;
920    }
921
922    /**
923     * Called when the panel key is released.
924     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
925     * @param event The key event.
926     */
927    public final void onKeyUpPanel(int featureId, KeyEvent event) {
928        // The panel key was released, so clear the chording key
929        if (mPanelChordingKey != 0) {
930            mPanelChordingKey = 0;
931
932            if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) {
933                return;
934            }
935
936            boolean playSoundEffect = false;
937            final PanelFeatureState st = getPanelState(featureId, true);
938            if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
939                    mActionBar.isOverflowReserved() &&
940                    !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {
941                if (mActionBar.getVisibility() == View.VISIBLE) {
942                    if (!mActionBar.isOverflowMenuShowing()) {
943                        if (!isDestroyed() && preparePanel(st, event)) {
944                            playSoundEffect = mActionBar.showOverflowMenu();
945                        }
946                    } else {
947                        playSoundEffect = mActionBar.hideOverflowMenu();
948                    }
949                }
950            } else {
951                if (st.isOpen || st.isHandled) {
952
953                    // Play the sound effect if the user closed an open menu (and not if
954                    // they just released a menu shortcut)
955                    playSoundEffect = st.isOpen;
956
957                    // Close menu
958                    closePanel(st, true);
959
960                } else if (st.isPrepared) {
961                    boolean show = true;
962                    if (st.refreshMenuContent) {
963                        // Something may have invalidated the menu since we prepared it.
964                        // Re-prepare it to refresh.
965                        st.isPrepared = false;
966                        show = preparePanel(st, event);
967                    }
968
969                    if (show) {
970                        // Write 'menu opened' to event log
971                        EventLog.writeEvent(50001, 0);
972
973                        // Show menu
974                        openPanel(st, event);
975
976                        playSoundEffect = true;
977                    }
978                }
979            }
980
981            if (playSoundEffect) {
982                AudioManager audioManager = (AudioManager) getContext().getSystemService(
983                        Context.AUDIO_SERVICE);
984                if (audioManager != null) {
985                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
986                } else {
987                    Log.w(TAG, "Couldn't get audio manager");
988                }
989            }
990        }
991    }
992
993    @Override
994    public final void closeAllPanels() {
995        final ViewManager wm = getWindowManager();
996        if (wm == null) {
997            return;
998        }
999
1000        final PanelFeatureState[] panels = mPanels;
1001        final int N = panels != null ? panels.length : 0;
1002        for (int i = 0; i < N; i++) {
1003            final PanelFeatureState panel = panels[i];
1004            if (panel != null) {
1005                closePanel(panel, true);
1006            }
1007        }
1008
1009        closeContextMenu();
1010    }
1011
1012    /**
1013     * Closes the context menu. This notifies the menu logic of the close, along
1014     * with dismissing it from the UI.
1015     */
1016    private synchronized void closeContextMenu() {
1017        if (mContextMenu != null) {
1018            mContextMenu.close();
1019            dismissContextMenu();
1020        }
1021    }
1022
1023    /**
1024     * Dismisses just the context menu UI. To close the context menu, use
1025     * {@link #closeContextMenu()}.
1026     */
1027    private synchronized void dismissContextMenu() {
1028        mContextMenu = null;
1029
1030        if (mContextMenuHelper != null) {
1031            mContextMenuHelper.dismiss();
1032            mContextMenuHelper = null;
1033        }
1034    }
1035
1036    @Override
1037    public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
1038        return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags);
1039    }
1040
1041    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
1042            int flags) {
1043        if (event.isSystem() || (st == null)) {
1044            return false;
1045        }
1046
1047        boolean handled = false;
1048
1049        // Only try to perform menu shortcuts if preparePanel returned true (possible false
1050        // return value from application not wanting to show the menu).
1051        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
1052            // The menu is prepared now, perform the shortcut on it
1053            handled = st.menu.performShortcut(keyCode, event, flags);
1054        }
1055
1056        if (handled) {
1057            // Mark as handled
1058            st.isHandled = true;
1059
1060            // Only close down the menu if we don't have an action bar keeping it open.
1061            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mActionBar == null) {
1062                closePanel(st, true);
1063            }
1064        }
1065
1066        return handled;
1067    }
1068
1069    @Override
1070    public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
1071
1072        PanelFeatureState st = getPanelState(featureId, true);
1073        if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) {
1074            return false;
1075        }
1076        if (st.menu == null) {
1077            return false;
1078        }
1079
1080        boolean res = st.menu.performIdentifierAction(id, flags);
1081
1082        // Only close down the menu if we don't have an action bar keeping it open.
1083        if (mActionBar == null) {
1084            closePanel(st, true);
1085        }
1086
1087        return res;
1088    }
1089
1090    public PanelFeatureState findMenuPanel(Menu menu) {
1091        final PanelFeatureState[] panels = mPanels;
1092        final int N = panels != null ? panels.length : 0;
1093        for (int i = 0; i < N; i++) {
1094            final PanelFeatureState panel = panels[i];
1095            if (panel != null && panel.menu == menu) {
1096                return panel;
1097            }
1098        }
1099        return null;
1100    }
1101
1102    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
1103        final Callback cb = getCallback();
1104        if (cb != null && !isDestroyed()) {
1105            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
1106            if (panel != null) {
1107                return cb.onMenuItemSelected(panel.featureId, item);
1108            }
1109        }
1110        return false;
1111    }
1112
1113    public void onMenuModeChange(MenuBuilder menu) {
1114        reopenMenu(true);
1115    }
1116
1117    private void reopenMenu(boolean toggleMenuMode) {
1118        if (mActionBar != null && mActionBar.isOverflowReserved() &&
1119                (!ViewConfiguration.get(getContext()).hasPermanentMenuKey() ||
1120                        mActionBar.isOverflowMenuShowPending())) {
1121            final Callback cb = getCallback();
1122            if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
1123                if (cb != null && !isDestroyed() && mActionBar.getVisibility() == View.VISIBLE) {
1124                    // If we have a menu invalidation pending, do it now.
1125                    if (mInvalidatePanelMenuPosted &&
1126                            (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
1127                        mDecor.removeCallbacks(mInvalidatePanelMenuRunnable);
1128                        mInvalidatePanelMenuRunnable.run();
1129                    }
1130
1131                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1132
1133                    // If we don't have a menu or we're waiting for a full content refresh,
1134                    // forget it. This is a lingering event that no longer matters.
1135                    if (st.menu != null && !st.refreshMenuContent &&
1136                            cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
1137                        cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
1138                        mActionBar.showOverflowMenu();
1139                    }
1140                }
1141            } else {
1142                mActionBar.hideOverflowMenu();
1143                if (cb != null && !isDestroyed()) {
1144                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1145                    cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
1146                }
1147            }
1148            return;
1149        }
1150
1151        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1152
1153        // Save the future expanded mode state since closePanel will reset it
1154        boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;
1155
1156        st.refreshDecorView = true;
1157        closePanel(st, false);
1158
1159        // Set the expanded mode state
1160        st.isInExpandedMode = newExpandedMode;
1161
1162        openPanel(st, null);
1163    }
1164
1165    /**
1166     * Initializes the menu associated with the given panel feature state. You
1167     * must at the very least set PanelFeatureState.menu to the Menu to be
1168     * associated with the given panel state. The default implementation creates
1169     * a new menu for the panel state.
1170     *
1171     * @param st The panel whose menu is being initialized.
1172     * @return Whether the initialization was successful.
1173     */
1174    protected boolean initializePanelMenu(final PanelFeatureState st) {
1175        Context context = getContext();
1176
1177        // If we have an action bar, initialize the menu with a context themed for it.
1178        if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) &&
1179                mActionBar != null) {
1180            TypedValue outValue = new TypedValue();
1181            Resources.Theme currentTheme = context.getTheme();
1182            currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme,
1183                    outValue, true);
1184            final int targetThemeRes = outValue.resourceId;
1185
1186            if (targetThemeRes != 0 && context.getThemeResId() != targetThemeRes) {
1187                context = new ContextThemeWrapper(context, targetThemeRes);
1188            }
1189        }
1190
1191        final MenuBuilder menu = new MenuBuilder(context);
1192
1193        menu.setCallback(this);
1194        st.setMenu(menu);
1195
1196        return true;
1197    }
1198
1199    /**
1200     * Perform initial setup of a panel. This should at the very least set the
1201     * style information in the PanelFeatureState and must set
1202     * PanelFeatureState.decor to the panel's window decor view.
1203     *
1204     * @param st The panel being initialized.
1205     */
1206    protected boolean initializePanelDecor(PanelFeatureState st) {
1207        st.decorView = new DecorView(getContext(), st.featureId);
1208        st.gravity = Gravity.CENTER | Gravity.BOTTOM;
1209        st.setStyle(getContext());
1210
1211        return true;
1212    }
1213
1214    /**
1215     * Determine the gravity value for the options panel. This can
1216     * differ in compact mode.
1217     *
1218     * @return gravity value to use for the panel window
1219     */
1220    private int getOptionsPanelGravity() {
1221        try {
1222            return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity();
1223        } catch (RemoteException ex) {
1224            Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex);
1225            return Gravity.CENTER | Gravity.BOTTOM;
1226        }
1227    }
1228
1229    void onOptionsPanelRotationChanged() {
1230        final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
1231        if (st == null) return;
1232
1233        final WindowManager.LayoutParams lp = st.decorView != null ?
1234                (WindowManager.LayoutParams) st.decorView.getLayoutParams() : null;
1235        if (lp != null) {
1236            lp.gravity = getOptionsPanelGravity();
1237            final ViewManager wm = getWindowManager();
1238            if (wm != null) {
1239                wm.updateViewLayout(st.decorView, lp);
1240            }
1241        }
1242    }
1243
1244    /**
1245     * Initializes the panel associated with the panel feature state. You must
1246     * at the very least set PanelFeatureState.panel to the View implementing
1247     * its contents. The default implementation gets the panel from the menu.
1248     *
1249     * @param st The panel state being initialized.
1250     * @return Whether the initialization was successful.
1251     */
1252    protected boolean initializePanelContent(PanelFeatureState st) {
1253        if (st.createdPanelView != null) {
1254            st.shownPanelView = st.createdPanelView;
1255            return true;
1256        }
1257
1258        if (st.menu == null) {
1259            return false;
1260        }
1261
1262        if (mPanelMenuPresenterCallback == null) {
1263            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
1264        }
1265
1266        MenuView menuView = st.isInListMode()
1267                ? st.getListMenuView(getContext(), mPanelMenuPresenterCallback)
1268                : st.getIconMenuView(getContext(), mPanelMenuPresenterCallback);
1269
1270        st.shownPanelView = (View) menuView;
1271
1272        if (st.shownPanelView != null) {
1273            // Use the menu View's default animations if it has any
1274            final int defaultAnimations = menuView.getWindowAnimations();
1275            if (defaultAnimations != 0) {
1276                st.windowAnimations = defaultAnimations;
1277            }
1278            return true;
1279        } else {
1280            return false;
1281        }
1282    }
1283
1284    @Override
1285    public boolean performContextMenuIdentifierAction(int id, int flags) {
1286        return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false;
1287    }
1288
1289    @Override
1290    public final void setBackgroundDrawable(Drawable drawable) {
1291        if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {
1292            mBackgroundResource = 0;
1293            mBackgroundDrawable = drawable;
1294            if (mDecor != null) {
1295                mDecor.setWindowBackground(drawable);
1296            }
1297        }
1298    }
1299
1300    @Override
1301    public final void setFeatureDrawableResource(int featureId, int resId) {
1302        if (resId != 0) {
1303            DrawableFeatureState st = getDrawableState(featureId, true);
1304            if (st.resid != resId) {
1305                st.resid = resId;
1306                st.uri = null;
1307                st.local = getContext().getDrawable(resId);
1308                updateDrawable(featureId, st, false);
1309            }
1310        } else {
1311            setFeatureDrawable(featureId, null);
1312        }
1313    }
1314
1315    @Override
1316    public final void setFeatureDrawableUri(int featureId, Uri uri) {
1317        if (uri != null) {
1318            DrawableFeatureState st = getDrawableState(featureId, true);
1319            if (st.uri == null || !st.uri.equals(uri)) {
1320                st.resid = 0;
1321                st.uri = uri;
1322                st.local = loadImageURI(uri);
1323                updateDrawable(featureId, st, false);
1324            }
1325        } else {
1326            setFeatureDrawable(featureId, null);
1327        }
1328    }
1329
1330    @Override
1331    public final void setFeatureDrawable(int featureId, Drawable drawable) {
1332        DrawableFeatureState st = getDrawableState(featureId, true);
1333        st.resid = 0;
1334        st.uri = null;
1335        if (st.local != drawable) {
1336            st.local = drawable;
1337            updateDrawable(featureId, st, false);
1338        }
1339    }
1340
1341    @Override
1342    public void setFeatureDrawableAlpha(int featureId, int alpha) {
1343        DrawableFeatureState st = getDrawableState(featureId, true);
1344        if (st.alpha != alpha) {
1345            st.alpha = alpha;
1346            updateDrawable(featureId, st, false);
1347        }
1348    }
1349
1350    protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) {
1351        DrawableFeatureState st = getDrawableState(featureId, true);
1352        if (st.def != drawable) {
1353            st.def = drawable;
1354            updateDrawable(featureId, st, false);
1355        }
1356    }
1357
1358    @Override
1359    public final void setFeatureInt(int featureId, int value) {
1360        // XXX Should do more management (as with drawable features) to
1361        // deal with interactions between multiple window policies.
1362        updateInt(featureId, value, false);
1363    }
1364
1365    /**
1366     * Update the state of a drawable feature. This should be called, for every
1367     * drawable feature supported, as part of onActive(), to make sure that the
1368     * contents of a containing window is properly updated.
1369     *
1370     * @see #onActive
1371     * @param featureId The desired drawable feature to change.
1372     * @param fromActive Always true when called from onActive().
1373     */
1374    protected final void updateDrawable(int featureId, boolean fromActive) {
1375        final DrawableFeatureState st = getDrawableState(featureId, false);
1376        if (st != null) {
1377            updateDrawable(featureId, st, fromActive);
1378        }
1379    }
1380
1381    /**
1382     * Called when a Drawable feature changes, for the window to update its
1383     * graphics.
1384     *
1385     * @param featureId The feature being changed.
1386     * @param drawable The new Drawable to show, or null if none.
1387     * @param alpha The new alpha blending of the Drawable.
1388     */
1389    protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) {
1390        ImageView view;
1391        if (featureId == FEATURE_LEFT_ICON) {
1392            view = getLeftIconView();
1393        } else if (featureId == FEATURE_RIGHT_ICON) {
1394            view = getRightIconView();
1395        } else {
1396            return;
1397        }
1398
1399        if (drawable != null) {
1400            drawable.setAlpha(alpha);
1401            view.setImageDrawable(drawable);
1402            view.setVisibility(View.VISIBLE);
1403        } else {
1404            view.setVisibility(View.GONE);
1405        }
1406    }
1407
1408    /**
1409     * Called when an int feature changes, for the window to update its
1410     * graphics.
1411     *
1412     * @param featureId The feature being changed.
1413     * @param value The new integer value.
1414     */
1415    protected void onIntChanged(int featureId, int value) {
1416        if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) {
1417            updateProgressBars(value);
1418        } else if (featureId == FEATURE_CUSTOM_TITLE) {
1419            FrameLayout titleContainer = (FrameLayout) findViewById(com.android.internal.R.id.title_container);
1420            if (titleContainer != null) {
1421                mLayoutInflater.inflate(value, titleContainer);
1422            }
1423        }
1424    }
1425
1426    /**
1427     * Updates the progress bars that are shown in the title bar.
1428     *
1429     * @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON},
1430     *            {@link Window#PROGRESS_VISIBILITY_OFF},
1431     *            {@link Window#PROGRESS_INDETERMINATE_ON},
1432     *            {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value
1433     *            starting at {@link Window#PROGRESS_START} through
1434     *            {@link Window#PROGRESS_END} for setting the default
1435     *            progress (if {@link Window#PROGRESS_END} is given,
1436     *            the progress bar widgets in the title will be hidden after an
1437     *            animation), a value between
1438     *            {@link Window#PROGRESS_SECONDARY_START} -
1439     *            {@link Window#PROGRESS_SECONDARY_END} for the
1440     *            secondary progress (if
1441     *            {@link Window#PROGRESS_SECONDARY_END} is given, the
1442     *            progress bar widgets will still be shown with the secondary
1443     *            progress bar will be completely filled in.)
1444     */
1445    private void updateProgressBars(int value) {
1446        ProgressBar circularProgressBar = getCircularProgressBar(true);
1447        ProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
1448
1449        final int features = getLocalFeatures();
1450        if (value == PROGRESS_VISIBILITY_ON) {
1451            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
1452                int level = horizontalProgressBar.getProgress();
1453                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
1454                        View.VISIBLE : View.INVISIBLE;
1455                horizontalProgressBar.setVisibility(visibility);
1456            }
1457            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
1458                circularProgressBar.setVisibility(View.VISIBLE);
1459            }
1460        } else if (value == PROGRESS_VISIBILITY_OFF) {
1461            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
1462                horizontalProgressBar.setVisibility(View.GONE);
1463            }
1464            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
1465                circularProgressBar.setVisibility(View.GONE);
1466            }
1467        } else if (value == PROGRESS_INDETERMINATE_ON) {
1468            horizontalProgressBar.setIndeterminate(true);
1469        } else if (value == PROGRESS_INDETERMINATE_OFF) {
1470            horizontalProgressBar.setIndeterminate(false);
1471        } else if (PROGRESS_START <= value && value <= PROGRESS_END) {
1472            // We want to set the progress value before testing for visibility
1473            // so that when the progress bar becomes visible again, it has the
1474            // correct level.
1475            horizontalProgressBar.setProgress(value - PROGRESS_START);
1476
1477            if (value < PROGRESS_END) {
1478                showProgressBars(horizontalProgressBar, circularProgressBar);
1479            } else {
1480                hideProgressBars(horizontalProgressBar, circularProgressBar);
1481            }
1482        } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {
1483            horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
1484
1485            showProgressBars(horizontalProgressBar, circularProgressBar);
1486        }
1487
1488    }
1489
1490    private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
1491        final int features = getLocalFeatures();
1492        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
1493                spinnyProgressBar.getVisibility() == View.INVISIBLE) {
1494            spinnyProgressBar.setVisibility(View.VISIBLE);
1495        }
1496        // Only show the progress bars if the primary progress is not complete
1497        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
1498                horizontalProgressBar.getProgress() < 10000) {
1499            horizontalProgressBar.setVisibility(View.VISIBLE);
1500        }
1501    }
1502
1503    private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
1504        final int features = getLocalFeatures();
1505        Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out);
1506        anim.setDuration(1000);
1507        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
1508                spinnyProgressBar.getVisibility() == View.VISIBLE) {
1509            spinnyProgressBar.startAnimation(anim);
1510            spinnyProgressBar.setVisibility(View.INVISIBLE);
1511        }
1512        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
1513                horizontalProgressBar.getVisibility() == View.VISIBLE) {
1514            horizontalProgressBar.startAnimation(anim);
1515            horizontalProgressBar.setVisibility(View.INVISIBLE);
1516        }
1517    }
1518
1519    @Override
1520    public void setIcon(int resId) {
1521        mIconRes = resId;
1522        mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON;
1523        mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;
1524        if (mActionBar != null) {
1525            mActionBar.setIcon(resId);
1526        }
1527    }
1528
1529    @Override
1530    public void setDefaultIcon(int resId) {
1531        if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0) {
1532            return;
1533        }
1534        mIconRes = resId;
1535        if (mActionBar != null && (!mActionBar.hasIcon() ||
1536                (mResourcesSetFlags & FLAG_RESOURCE_SET_ICON_FALLBACK) != 0)) {
1537            if (resId != 0) {
1538                mActionBar.setIcon(resId);
1539                mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;
1540            } else {
1541                mActionBar.setIcon(getContext().getPackageManager().getDefaultActivityIcon());
1542                mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
1543            }
1544        }
1545    }
1546
1547    @Override
1548    public void setLogo(int resId) {
1549        mLogoRes = resId;
1550        mResourcesSetFlags |= FLAG_RESOURCE_SET_LOGO;
1551        if (mActionBar != null) {
1552            mActionBar.setLogo(resId);
1553        }
1554    }
1555
1556    @Override
1557    public void setDefaultLogo(int resId) {
1558        if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0) {
1559            return;
1560        }
1561        mLogoRes = resId;
1562        if (mActionBar != null && !mActionBar.hasLogo()) {
1563            mActionBar.setLogo(resId);
1564        }
1565    }
1566
1567    @Override
1568    public void setLocalFocus(boolean hasFocus, boolean inTouchMode) {
1569        getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode);
1570
1571    }
1572
1573    @Override
1574    public void injectInputEvent(InputEvent event) {
1575        getViewRootImpl().dispatchInputEvent(event);
1576    }
1577
1578    private ViewRootImpl getViewRootImpl() {
1579        if (mDecor != null) {
1580            ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
1581            if (viewRootImpl != null) {
1582                return viewRootImpl;
1583            }
1584        }
1585        throw new IllegalStateException("view not added");
1586    }
1587
1588    /**
1589     * Request that key events come to this activity. Use this if your activity
1590     * has no views with focus, but the activity still wants a chance to process
1591     * key events.
1592     */
1593    @Override
1594    public void takeKeyEvents(boolean get) {
1595        mDecor.setFocusable(get);
1596    }
1597
1598    @Override
1599    public boolean superDispatchKeyEvent(KeyEvent event) {
1600        return mDecor.superDispatchKeyEvent(event);
1601    }
1602
1603    @Override
1604    public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
1605        return mDecor.superDispatchKeyShortcutEvent(event);
1606    }
1607
1608    @Override
1609    public boolean superDispatchTouchEvent(MotionEvent event) {
1610        return mDecor.superDispatchTouchEvent(event);
1611    }
1612
1613    @Override
1614    public boolean superDispatchTrackballEvent(MotionEvent event) {
1615        return mDecor.superDispatchTrackballEvent(event);
1616    }
1617
1618    @Override
1619    public boolean superDispatchGenericMotionEvent(MotionEvent event) {
1620        return mDecor.superDispatchGenericMotionEvent(event);
1621    }
1622
1623    /**
1624     * A key was pressed down and not handled by anything else in the window.
1625     *
1626     * @see #onKeyUp
1627     * @see android.view.KeyEvent
1628     */
1629    protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {
1630        /* ****************************************************************************
1631         * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
1632         *
1633         * If your key handling must happen before the app gets a crack at the event,
1634         * it goes in PhoneWindowManager.
1635         *
1636         * If your key handling should happen in all windows, and does not depend on
1637         * the state of the current application, other than that the current
1638         * application can override the behavior by handling the event itself, it
1639         * should go in PhoneFallbackEventHandler.
1640         *
1641         * Only if your handling depends on the window, and the fact that it has
1642         * a DecorView, should it go here.
1643         * ****************************************************************************/
1644
1645        final KeyEvent.DispatcherState dispatcher =
1646                mDecor != null ? mDecor.getKeyDispatcherState() : null;
1647        //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()
1648        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
1649
1650        switch (keyCode) {
1651            case KeyEvent.KEYCODE_VOLUME_UP:
1652            case KeyEvent.KEYCODE_VOLUME_DOWN:
1653            case KeyEvent.KEYCODE_VOLUME_MUTE: {
1654                // Similar code is in PhoneFallbackEventHandler in case the window
1655                // doesn't have one of these.  In this case, we execute it here and
1656                // eat the event instead, because we have mVolumeControlStreamType
1657                // and they don't.
1658                getAudioManager().handleKeyDown(event, mVolumeControlStreamType);
1659                return true;
1660            }
1661
1662            case KeyEvent.KEYCODE_MENU: {
1663                onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
1664                return true;
1665            }
1666
1667            case KeyEvent.KEYCODE_BACK: {
1668                if (event.getRepeatCount() > 0) break;
1669                if (featureId < 0) break;
1670                // Currently don't do anything with long press.
1671                if (dispatcher != null) {
1672                    dispatcher.startTracking(event, this);
1673                }
1674                return true;
1675            }
1676
1677        }
1678
1679        return false;
1680    }
1681
1682    private KeyguardManager getKeyguardManager() {
1683        if (mKeyguardManager == null) {
1684            mKeyguardManager = (KeyguardManager) getContext().getSystemService(
1685                    Context.KEYGUARD_SERVICE);
1686        }
1687        return mKeyguardManager;
1688    }
1689
1690    AudioManager getAudioManager() {
1691        if (mAudioManager == null) {
1692            mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
1693        }
1694        return mAudioManager;
1695    }
1696
1697    /**
1698     * A key was released and not handled by anything else in the window.
1699     *
1700     * @see #onKeyDown
1701     * @see android.view.KeyEvent
1702     */
1703    protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) {
1704        final KeyEvent.DispatcherState dispatcher =
1705                mDecor != null ? mDecor.getKeyDispatcherState() : null;
1706        if (dispatcher != null) {
1707            dispatcher.handleUpEvent(event);
1708        }
1709        //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()
1710        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
1711
1712        switch (keyCode) {
1713            case KeyEvent.KEYCODE_VOLUME_UP:
1714            case KeyEvent.KEYCODE_VOLUME_DOWN:
1715            case KeyEvent.KEYCODE_VOLUME_MUTE: {
1716                // Similar code is in PhoneFallbackEventHandler in case the window
1717                // doesn't have one of these.  In this case, we execute it here and
1718                // eat the event instead, because we have mVolumeControlStreamType
1719                // and they don't.
1720                getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
1721                return true;
1722            }
1723
1724            case KeyEvent.KEYCODE_MENU: {
1725                onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
1726                        event);
1727                return true;
1728            }
1729
1730            case KeyEvent.KEYCODE_BACK: {
1731                if (featureId < 0) break;
1732                if (event.isTracking() && !event.isCanceled()) {
1733                    if (featureId == FEATURE_OPTIONS_PANEL) {
1734                        PanelFeatureState st = getPanelState(featureId, false);
1735                        if (st != null && st.isInExpandedMode) {
1736                            // If the user is in an expanded menu and hits back, it
1737                            // should go back to the icon menu
1738                            reopenMenu(true);
1739                            return true;
1740                        }
1741                    }
1742                    closePanel(featureId);
1743                    return true;
1744                }
1745                break;
1746            }
1747
1748            case KeyEvent.KEYCODE_SEARCH: {
1749                /*
1750                 * Do this in onKeyUp since the Search key is also used for
1751                 * chording quick launch shortcuts.
1752                 */
1753                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
1754                    break;
1755                }
1756                if (event.isTracking() && !event.isCanceled()) {
1757                    launchDefaultSearch();
1758                }
1759                return true;
1760            }
1761        }
1762
1763        return false;
1764    }
1765
1766    @Override
1767    protected void onActive() {
1768    }
1769
1770    @Override
1771    public final View getDecorView() {
1772        if (mDecor == null) {
1773            installDecor();
1774        }
1775        return mDecor;
1776    }
1777
1778    @Override
1779    public final View peekDecorView() {
1780        return mDecor;
1781    }
1782
1783    static private final String FOCUSED_ID_TAG = "android:focusedViewId";
1784    static private final String VIEWS_TAG = "android:views";
1785    static private final String PANELS_TAG = "android:Panels";
1786    static private final String ACTION_BAR_TAG = "android:ActionBar";
1787
1788    /** {@inheritDoc} */
1789    @Override
1790    public Bundle saveHierarchyState() {
1791        Bundle outState = new Bundle();
1792        if (mContentParent == null) {
1793            return outState;
1794        }
1795
1796        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
1797        mContentParent.saveHierarchyState(states);
1798        outState.putSparseParcelableArray(VIEWS_TAG, states);
1799
1800        // save the focused view id
1801        View focusedView = mContentParent.findFocus();
1802        if (focusedView != null) {
1803            if (focusedView.getId() != View.NO_ID) {
1804                outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
1805            } else {
1806                if (false) {
1807                    Log.d(TAG, "couldn't save which view has focus because the focused view "
1808                            + focusedView + " has no id.");
1809                }
1810            }
1811        }
1812
1813        // save the panels
1814        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
1815        savePanelState(panelStates);
1816        if (panelStates.size() > 0) {
1817            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
1818        }
1819
1820        if (mActionBar != null) {
1821            SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
1822            mActionBar.saveHierarchyState(actionBarStates);
1823            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
1824        }
1825
1826        return outState;
1827    }
1828
1829    /** {@inheritDoc} */
1830    @Override
1831    public void restoreHierarchyState(Bundle savedInstanceState) {
1832        if (mContentParent == null) {
1833            return;
1834        }
1835
1836        SparseArray<Parcelable> savedStates
1837                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
1838        if (savedStates != null) {
1839            mContentParent.restoreHierarchyState(savedStates);
1840        }
1841
1842        // restore the focused view
1843        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
1844        if (focusedViewId != View.NO_ID) {
1845            View needsFocus = mContentParent.findViewById(focusedViewId);
1846            if (needsFocus != null) {
1847                needsFocus.requestFocus();
1848            } else {
1849                Log.w(TAG,
1850                        "Previously focused view reported id " + focusedViewId
1851                                + " during save, but can't be found during restore.");
1852            }
1853        }
1854
1855        // restore the panels
1856        SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
1857        if (panelStates != null) {
1858            restorePanelState(panelStates);
1859        }
1860
1861        if (mActionBar != null) {
1862            SparseArray<Parcelable> actionBarStates =
1863                    savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
1864            if (actionBarStates != null) {
1865                mActionBar.restoreHierarchyState(actionBarStates);
1866            } else {
1867                Log.w(TAG, "Missing saved instance states for action bar views! " +
1868                        "State will not be restored.");
1869            }
1870        }
1871    }
1872
1873    /**
1874     * Invoked when the panels should freeze their state.
1875     *
1876     * @param icicles Save state into this. This is usually indexed by the
1877     *            featureId. This will be given to {@link #restorePanelState} in the
1878     *            future.
1879     */
1880    private void savePanelState(SparseArray<Parcelable> icicles) {
1881        PanelFeatureState[] panels = mPanels;
1882        if (panels == null) {
1883            return;
1884        }
1885
1886        for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) {
1887            if (panels[curFeatureId] != null) {
1888                icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState());
1889            }
1890        }
1891    }
1892
1893    /**
1894     * Invoked when the panels should thaw their state from a previously frozen state.
1895     *
1896     * @param icicles The state saved by {@link #savePanelState} that needs to be thawed.
1897     */
1898    private void restorePanelState(SparseArray<Parcelable> icicles) {
1899        PanelFeatureState st;
1900        int curFeatureId;
1901        for (int i = icicles.size() - 1; i >= 0; i--) {
1902            curFeatureId = icicles.keyAt(i);
1903            st = getPanelState(curFeatureId, false /* required */);
1904            if (st == null) {
1905                // The panel must not have been required, and is currently not around, skip it
1906                continue;
1907            }
1908
1909            st.onRestoreInstanceState(icicles.get(curFeatureId));
1910            invalidatePanelMenu(curFeatureId);
1911        }
1912
1913        /*
1914         * Implementation note: call openPanelsAfterRestore later to actually open the
1915         * restored panels.
1916         */
1917    }
1918
1919    /**
1920     * Opens the panels that have had their state restored. This should be
1921     * called sometime after {@link #restorePanelState} when it is safe to add
1922     * to the window manager.
1923     */
1924    private void openPanelsAfterRestore() {
1925        PanelFeatureState[] panels = mPanels;
1926
1927        if (panels == null) {
1928            return;
1929        }
1930
1931        PanelFeatureState st;
1932        for (int i = panels.length - 1; i >= 0; i--) {
1933            st = panels[i];
1934            // We restore the panel if it was last open; we skip it if it
1935            // now is open, to avoid a race condition if the user immediately
1936            // opens it when we are resuming.
1937            if (st != null) {
1938                st.applyFrozenState();
1939                if (!st.isOpen && st.wasLastOpen) {
1940                    st.isInExpandedMode = st.wasLastExpanded;
1941                    openPanel(st, null);
1942                }
1943            }
1944        }
1945    }
1946
1947    private class PanelMenuPresenterCallback implements MenuPresenter.Callback {
1948        @Override
1949        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1950            final Menu parentMenu = menu.getRootMenu();
1951            final boolean isSubMenu = parentMenu != menu;
1952            final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);
1953            if (panel != null) {
1954                if (isSubMenu) {
1955                    callOnPanelClosed(panel.featureId, panel, parentMenu);
1956                    closePanel(panel, true);
1957                } else {
1958                    // Close the panel and only do the callback if the menu is being
1959                    // closed completely, not if opening a sub menu
1960                    closePanel(panel, allMenusAreClosing);
1961                }
1962            }
1963        }
1964
1965        @Override
1966        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1967            if (subMenu == null && hasFeature(FEATURE_ACTION_BAR)) {
1968                Callback cb = getCallback();
1969                if (cb != null && !isDestroyed()) {
1970                    cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
1971                }
1972            }
1973
1974            return true;
1975        }
1976    }
1977
1978    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
1979        @Override
1980        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1981            Callback cb = getCallback();
1982            if (cb != null) {
1983                cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
1984                return true;
1985            }
1986            return false;
1987        }
1988
1989        @Override
1990        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1991            checkCloseActionMenu(menu);
1992        }
1993    }
1994
1995    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
1996        /* package */int mDefaultOpacity = PixelFormat.OPAQUE;
1997
1998        /** The feature ID of the panel, or -1 if this is the application's DecorView */
1999        private final int mFeatureId;
2000
2001        private final Rect mDrawingBounds = new Rect();
2002
2003        private final Rect mBackgroundPadding = new Rect();
2004
2005        private final Rect mFramePadding = new Rect();
2006
2007        private final Rect mFrameOffsets = new Rect();
2008
2009        private boolean mChanging;
2010
2011        private Drawable mMenuBackground;
2012        private boolean mWatchingForMenu;
2013        private int mDownY;
2014
2015        private ActionMode mActionMode;
2016        private ActionBarContextView mActionModeView;
2017        private PopupWindow mActionModePopup;
2018        private Runnable mShowActionModePopup;
2019
2020        // View added at runtime to draw under the status bar area
2021        private View mStatusGuard;
2022        // View added at runtime to draw under the navigation bar area
2023        private View mNavigationGuard;
2024
2025        public DecorView(Context context, int featureId) {
2026            super(context);
2027            mFeatureId = featureId;
2028        }
2029
2030        @Override
2031        public boolean dispatchKeyEvent(KeyEvent event) {
2032            final int keyCode = event.getKeyCode();
2033            final int action = event.getAction();
2034            final boolean isDown = action == KeyEvent.ACTION_DOWN;
2035
2036            if (isDown && (event.getRepeatCount() == 0)) {
2037                // First handle chording of panel key: if a panel key is held
2038                // but not released, try to execute a shortcut in it.
2039                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
2040                    boolean handled = dispatchKeyShortcutEvent(event);
2041                    if (handled) {
2042                        return true;
2043                    }
2044                }
2045
2046                // If a panel is open, perform a shortcut on it without the
2047                // chorded panel key
2048                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
2049                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
2050                        return true;
2051                    }
2052                }
2053            }
2054
2055            if (!isDestroyed()) {
2056                final Callback cb = getCallback();
2057                final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
2058                        : super.dispatchKeyEvent(event);
2059                if (handled) {
2060                    return true;
2061                }
2062            }
2063
2064            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
2065                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
2066        }
2067
2068        @Override
2069        public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
2070            // If the panel is already prepared, then perform the shortcut using it.
2071            boolean handled;
2072            if (mPreparedPanel != null) {
2073                handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
2074                        Menu.FLAG_PERFORM_NO_CLOSE);
2075                if (handled) {
2076                    if (mPreparedPanel != null) {
2077                        mPreparedPanel.isHandled = true;
2078                    }
2079                    return true;
2080                }
2081            }
2082
2083            // Shortcut not handled by the panel.  Dispatch to the view hierarchy.
2084            final Callback cb = getCallback();
2085            handled = cb != null && !isDestroyed() && mFeatureId < 0
2086                    ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
2087            if (handled) {
2088                return true;
2089            }
2090
2091            // If the panel is not prepared, then we may be trying to handle a shortcut key
2092            // combination such as Control+C.  Temporarily prepare the panel then mark it
2093            // unprepared again when finished to ensure that the panel will again be prepared
2094            // the next time it is shown for real.
2095            if (mPreparedPanel == null) {
2096                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
2097                preparePanel(st, ev);
2098                handled = performPanelShortcut(st, ev.getKeyCode(), ev,
2099                        Menu.FLAG_PERFORM_NO_CLOSE);
2100                st.isPrepared = false;
2101                if (handled) {
2102                    return true;
2103                }
2104            }
2105            return false;
2106        }
2107
2108        @Override
2109        public boolean dispatchTouchEvent(MotionEvent ev) {
2110            final Callback cb = getCallback();
2111            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
2112                    : super.dispatchTouchEvent(ev);
2113        }
2114
2115        @Override
2116        public boolean dispatchTrackballEvent(MotionEvent ev) {
2117            final Callback cb = getCallback();
2118            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev)
2119                    : super.dispatchTrackballEvent(ev);
2120        }
2121
2122        @Override
2123        public boolean dispatchGenericMotionEvent(MotionEvent ev) {
2124            final Callback cb = getCallback();
2125            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev)
2126                    : super.dispatchGenericMotionEvent(ev);
2127        }
2128
2129        public boolean superDispatchKeyEvent(KeyEvent event) {
2130            if (super.dispatchKeyEvent(event)) {
2131                return true;
2132            }
2133
2134            // Not handled by the view hierarchy, does the action bar want it
2135            // to cancel out of something special?
2136            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
2137                final int action = event.getAction();
2138                // Back cancels action modes first.
2139                if (mActionMode != null) {
2140                    if (action == KeyEvent.ACTION_UP) {
2141                        mActionMode.finish();
2142                    }
2143                    return true;
2144                }
2145
2146                // Next collapse any expanded action views.
2147                if (mActionBar != null && mActionBar.hasExpandedActionView()) {
2148                    if (action == KeyEvent.ACTION_UP) {
2149                        mActionBar.collapseActionView();
2150                    }
2151                    return true;
2152                }
2153            }
2154
2155            return false;
2156        }
2157
2158        public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
2159            return super.dispatchKeyShortcutEvent(event);
2160        }
2161
2162        public boolean superDispatchTouchEvent(MotionEvent event) {
2163            return super.dispatchTouchEvent(event);
2164        }
2165
2166        public boolean superDispatchTrackballEvent(MotionEvent event) {
2167            return super.dispatchTrackballEvent(event);
2168        }
2169
2170        public boolean superDispatchGenericMotionEvent(MotionEvent event) {
2171            return super.dispatchGenericMotionEvent(event);
2172        }
2173
2174        @Override
2175        public boolean onTouchEvent(MotionEvent event) {
2176            return onInterceptTouchEvent(event);
2177        }
2178
2179        private boolean isOutOfBounds(int x, int y) {
2180            return x < -5 || y < -5 || x > (getWidth() + 5)
2181                    || y > (getHeight() + 5);
2182        }
2183
2184        @Override
2185        public boolean onInterceptTouchEvent(MotionEvent event) {
2186            int action = event.getAction();
2187            if (mFeatureId >= 0) {
2188                if (action == MotionEvent.ACTION_DOWN) {
2189                    int x = (int)event.getX();
2190                    int y = (int)event.getY();
2191                    if (isOutOfBounds(x, y)) {
2192                        closePanel(mFeatureId);
2193                        return true;
2194                    }
2195                }
2196            }
2197
2198            if (!SWEEP_OPEN_MENU) {
2199                return false;
2200            }
2201
2202            if (mFeatureId >= 0) {
2203                if (action == MotionEvent.ACTION_DOWN) {
2204                    Log.i(TAG, "Watchiing!");
2205                    mWatchingForMenu = true;
2206                    mDownY = (int) event.getY();
2207                    return false;
2208                }
2209
2210                if (!mWatchingForMenu) {
2211                    return false;
2212                }
2213
2214                int y = (int)event.getY();
2215                if (action == MotionEvent.ACTION_MOVE) {
2216                    if (y > (mDownY+30)) {
2217                        Log.i(TAG, "Closing!");
2218                        closePanel(mFeatureId);
2219                        mWatchingForMenu = false;
2220                        return true;
2221                    }
2222                } else if (action == MotionEvent.ACTION_UP) {
2223                    mWatchingForMenu = false;
2224                }
2225
2226                return false;
2227            }
2228
2229            //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
2230            //        + " (in " + getHeight() + ")");
2231
2232            if (action == MotionEvent.ACTION_DOWN) {
2233                int y = (int)event.getY();
2234                if (y >= (getHeight()-5) && !hasChildren()) {
2235                    Log.i(TAG, "Watchiing!");
2236                    mWatchingForMenu = true;
2237                }
2238                return false;
2239            }
2240
2241            if (!mWatchingForMenu) {
2242                return false;
2243            }
2244
2245            int y = (int)event.getY();
2246            if (action == MotionEvent.ACTION_MOVE) {
2247                if (y < (getHeight()-30)) {
2248                    Log.i(TAG, "Opening!");
2249                    openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
2250                            KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
2251                    mWatchingForMenu = false;
2252                    return true;
2253                }
2254            } else if (action == MotionEvent.ACTION_UP) {
2255                mWatchingForMenu = false;
2256            }
2257
2258            return false;
2259        }
2260
2261        @Override
2262        public void sendAccessibilityEvent(int eventType) {
2263            if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
2264                return;
2265            }
2266
2267            // if we are showing a feature that should be announced and one child
2268            // make this child the event source since this is the feature itself
2269            // otherwise the callback will take over and announce its client
2270            if ((mFeatureId == FEATURE_OPTIONS_PANEL ||
2271                    mFeatureId == FEATURE_CONTEXT_MENU ||
2272                    mFeatureId == FEATURE_PROGRESS ||
2273                    mFeatureId == FEATURE_INDETERMINATE_PROGRESS)
2274                    && getChildCount() == 1) {
2275                getChildAt(0).sendAccessibilityEvent(eventType);
2276            } else {
2277                super.sendAccessibilityEvent(eventType);
2278            }
2279        }
2280
2281        @Override
2282        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
2283            final Callback cb = getCallback();
2284            if (cb != null && !isDestroyed()) {
2285                if (cb.dispatchPopulateAccessibilityEvent(event)) {
2286                    return true;
2287                }
2288            }
2289            return super.dispatchPopulateAccessibilityEvent(event);
2290        }
2291
2292        @Override
2293        protected boolean setFrame(int l, int t, int r, int b) {
2294            boolean changed = super.setFrame(l, t, r, b);
2295            if (changed) {
2296                final Rect drawingBounds = mDrawingBounds;
2297                getDrawingRect(drawingBounds);
2298
2299                Drawable fg = getForeground();
2300                if (fg != null) {
2301                    final Rect frameOffsets = mFrameOffsets;
2302                    drawingBounds.left += frameOffsets.left;
2303                    drawingBounds.top += frameOffsets.top;
2304                    drawingBounds.right -= frameOffsets.right;
2305                    drawingBounds.bottom -= frameOffsets.bottom;
2306                    fg.setBounds(drawingBounds);
2307                    final Rect framePadding = mFramePadding;
2308                    drawingBounds.left += framePadding.left - frameOffsets.left;
2309                    drawingBounds.top += framePadding.top - frameOffsets.top;
2310                    drawingBounds.right -= framePadding.right - frameOffsets.right;
2311                    drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
2312                }
2313
2314                Drawable bg = getBackground();
2315                if (bg != null) {
2316                    bg.setBounds(drawingBounds);
2317                }
2318
2319                if (SWEEP_OPEN_MENU) {
2320                    if (mMenuBackground == null && mFeatureId < 0
2321                            && getAttributes().height
2322                            == WindowManager.LayoutParams.MATCH_PARENT) {
2323                        mMenuBackground = getContext().getDrawable(
2324                                com.android.internal.R.drawable.menu_background);
2325                    }
2326                    if (mMenuBackground != null) {
2327                        mMenuBackground.setBounds(drawingBounds.left,
2328                                drawingBounds.bottom-6, drawingBounds.right,
2329                                drawingBounds.bottom+20);
2330                    }
2331                }
2332            }
2333            return changed;
2334        }
2335
2336        @Override
2337        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
2338            final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
2339            final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
2340
2341            final int widthMode = getMode(widthMeasureSpec);
2342            final int heightMode = getMode(heightMeasureSpec);
2343
2344            boolean fixedWidth = false;
2345            if (widthMode == AT_MOST) {
2346                final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
2347                if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
2348                    final int w;
2349                    if (tvw.type == TypedValue.TYPE_DIMENSION) {
2350                        w = (int) tvw.getDimension(metrics);
2351                    } else if (tvw.type == TypedValue.TYPE_FRACTION) {
2352                        w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
2353                    } else {
2354                        w = 0;
2355                    }
2356
2357                    if (w > 0) {
2358                        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
2359                        widthMeasureSpec = MeasureSpec.makeMeasureSpec(
2360                                Math.min(w, widthSize), EXACTLY);
2361                        fixedWidth = true;
2362                    }
2363                }
2364            }
2365
2366            if (heightMode == AT_MOST) {
2367                final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
2368                if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
2369                    final int h;
2370                    if (tvh.type == TypedValue.TYPE_DIMENSION) {
2371                        h = (int) tvh.getDimension(metrics);
2372                    } else if (tvh.type == TypedValue.TYPE_FRACTION) {
2373                        h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
2374                    } else {
2375                        h = 0;
2376                    }
2377                    if (h > 0) {
2378                        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
2379                        heightMeasureSpec = MeasureSpec.makeMeasureSpec(
2380                                Math.min(h, heightSize), EXACTLY);
2381                    }
2382                }
2383            }
2384
2385            if (mOutsetBottom != null) {
2386                int mode = MeasureSpec.getMode(heightMeasureSpec);
2387                if (mode != MeasureSpec.UNSPECIFIED) {
2388                    int outset = (int) mOutsetBottom.getDimension(metrics);
2389                    int height = MeasureSpec.getSize(heightMeasureSpec);
2390                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + outset, mode);
2391                }
2392            }
2393
2394            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
2395
2396            int width = getMeasuredWidth();
2397            boolean measure = false;
2398
2399            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
2400
2401            if (!fixedWidth && widthMode == AT_MOST) {
2402                final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
2403                if (tv.type != TypedValue.TYPE_NULL) {
2404                    final int min;
2405                    if (tv.type == TypedValue.TYPE_DIMENSION) {
2406                        min = (int)tv.getDimension(metrics);
2407                    } else if (tv.type == TypedValue.TYPE_FRACTION) {
2408                        min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
2409                    } else {
2410                        min = 0;
2411                    }
2412
2413                    if (width < min) {
2414                        widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
2415                        measure = true;
2416                    }
2417                }
2418            }
2419
2420            // TODO: Support height?
2421
2422            if (measure) {
2423                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
2424            }
2425        }
2426
2427        @Override
2428        public void draw(Canvas canvas) {
2429            super.draw(canvas);
2430
2431            if (mMenuBackground != null) {
2432                mMenuBackground.draw(canvas);
2433            }
2434        }
2435
2436
2437        @Override
2438        public boolean showContextMenuForChild(View originalView) {
2439            // Reuse the context menu builder
2440            if (mContextMenu == null) {
2441                mContextMenu = new ContextMenuBuilder(getContext());
2442                mContextMenu.setCallback(mContextMenuCallback);
2443            } else {
2444                mContextMenu.clearAll();
2445            }
2446
2447            final MenuDialogHelper helper = mContextMenu.show(originalView,
2448                    originalView.getWindowToken());
2449            if (helper != null) {
2450                helper.setPresenterCallback(mContextMenuCallback);
2451            } else if (mContextMenuHelper != null) {
2452                // No menu to show, but if we have a menu currently showing it just became blank.
2453                // Close it.
2454                mContextMenuHelper.dismiss();
2455            }
2456            mContextMenuHelper = helper;
2457            return helper != null;
2458        }
2459
2460        @Override
2461        public ActionMode startActionModeForChild(View originalView,
2462                ActionMode.Callback callback) {
2463            // originalView can be used here to be sure that we don't obscure
2464            // relevant content with the context mode UI.
2465            return startActionMode(callback);
2466        }
2467
2468        @Override
2469        public ActionMode startActionMode(ActionMode.Callback callback) {
2470            if (mActionMode != null) {
2471                mActionMode.finish();
2472            }
2473
2474            final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
2475            ActionMode mode = null;
2476            if (getCallback() != null && !isDestroyed()) {
2477                try {
2478                    mode = getCallback().onWindowStartingActionMode(wrappedCallback);
2479                } catch (AbstractMethodError ame) {
2480                    // Older apps might not implement this callback method.
2481                }
2482            }
2483            if (mode != null) {
2484                mActionMode = mode;
2485            } else {
2486                if (mActionModeView == null) {
2487                    if (isFloating()) {
2488                        mActionModeView = new ActionBarContextView(mContext);
2489                        mActionModePopup = new PopupWindow(mContext, null,
2490                                com.android.internal.R.attr.actionModePopupWindowStyle);
2491                        mActionModePopup.setWindowLayoutType(
2492                                WindowManager.LayoutParams.TYPE_APPLICATION);
2493                        mActionModePopup.setContentView(mActionModeView);
2494                        mActionModePopup.setWidth(MATCH_PARENT);
2495
2496                        TypedValue heightValue = new TypedValue();
2497                        mContext.getTheme().resolveAttribute(
2498                                com.android.internal.R.attr.actionBarSize, heightValue, true);
2499                        final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
2500                                mContext.getResources().getDisplayMetrics());
2501                        mActionModeView.setContentHeight(height);
2502                        mActionModePopup.setHeight(WRAP_CONTENT);
2503                        mShowActionModePopup = new Runnable() {
2504                            public void run() {
2505                                mActionModePopup.showAtLocation(
2506                                        mActionModeView.getApplicationWindowToken(),
2507                                        Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
2508                            }
2509                        };
2510                    } else {
2511                        ViewStub stub = (ViewStub) findViewById(
2512                                com.android.internal.R.id.action_mode_bar_stub);
2513                        if (stub != null) {
2514                            mActionModeView = (ActionBarContextView) stub.inflate();
2515                        }
2516                    }
2517                }
2518
2519                if (mActionModeView != null) {
2520                    mActionModeView.killMode();
2521                    mode = new StandaloneActionMode(getContext(), mActionModeView, wrappedCallback,
2522                            mActionModePopup == null);
2523                    if (callback.onCreateActionMode(mode, mode.getMenu())) {
2524                        mode.invalidate();
2525                        mActionModeView.initForMode(mode);
2526                        mActionModeView.setVisibility(View.VISIBLE);
2527                        mActionMode = mode;
2528                        if (mActionModePopup != null) {
2529                            post(mShowActionModePopup);
2530                        }
2531                        mActionModeView.sendAccessibilityEvent(
2532                                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2533                    } else {
2534                        mActionMode = null;
2535                    }
2536                }
2537            }
2538            if (mActionMode != null && getCallback() != null && !isDestroyed()) {
2539                try {
2540                    getCallback().onActionModeStarted(mActionMode);
2541                } catch (AbstractMethodError ame) {
2542                    // Older apps might not implement this callback method.
2543                }
2544            }
2545            return mActionMode;
2546        }
2547
2548        public void startChanging() {
2549            mChanging = true;
2550        }
2551
2552        public void finishChanging() {
2553            mChanging = false;
2554            drawableChanged();
2555        }
2556
2557        public void setWindowBackground(Drawable drawable) {
2558            if (getBackground() != drawable) {
2559                setBackgroundDrawable(drawable);
2560                if (drawable != null) {
2561                    drawable.getPadding(mBackgroundPadding);
2562                } else {
2563                    mBackgroundPadding.setEmpty();
2564                }
2565                drawableChanged();
2566            }
2567        }
2568
2569        @Override
2570        public void setBackgroundDrawable(Drawable d) {
2571            super.setBackgroundDrawable(d);
2572            if (getWindowToken() != null) {
2573                updateWindowResizeState();
2574            }
2575        }
2576
2577        public void setWindowFrame(Drawable drawable) {
2578            if (getForeground() != drawable) {
2579                setForeground(drawable);
2580                if (drawable != null) {
2581                    drawable.getPadding(mFramePadding);
2582                } else {
2583                    mFramePadding.setEmpty();
2584                }
2585                drawableChanged();
2586            }
2587        }
2588
2589        @Override
2590        protected boolean fitSystemWindows(Rect insets) {
2591            mFrameOffsets.set(insets);
2592            updateStatusGuard(insets);
2593            updateNavigationGuard(insets);
2594            if (getForeground() != null) {
2595                drawableChanged();
2596            }
2597            return super.fitSystemWindows(insets);
2598        }
2599
2600        @Override
2601        public boolean isTransitionGroup() {
2602            return false;
2603        }
2604
2605        private void updateStatusGuard(Rect insets) {
2606            boolean showStatusGuard = false;
2607            // Show the status guard when the non-overlay contextual action bar is showing
2608            if (mActionModeView != null) {
2609                if (mActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
2610                    MarginLayoutParams mlp = (MarginLayoutParams) mActionModeView.getLayoutParams();
2611                    boolean mlpChanged = false;
2612                    final boolean nonOverlayShown =
2613                            (getLocalFeatures() & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0
2614                            && mActionModeView.isShown();
2615                    if (nonOverlayShown) {
2616                        // set top margin to top insets, show status guard
2617                        if (mlp.topMargin != insets.top) {
2618                            mlpChanged = true;
2619                            mlp.topMargin = insets.top;
2620                            if (mStatusGuard == null) {
2621                                mStatusGuard = new View(mContext);
2622                                mStatusGuard.setBackgroundColor(mContext.getResources()
2623                                        .getColor(R.color.input_method_navigation_guard));
2624                                addView(mStatusGuard, new LayoutParams(
2625                                        LayoutParams.MATCH_PARENT, mlp.topMargin,
2626                                        Gravity.START | Gravity.TOP));
2627                            } else {
2628                                LayoutParams lp = (LayoutParams) mStatusGuard.getLayoutParams();
2629                                if (lp.height != mlp.topMargin) {
2630                                    lp.height = mlp.topMargin;
2631                                    mStatusGuard.setLayoutParams(lp);
2632                                }
2633                            }
2634                        }
2635                        insets.top = 0;  // consume top insets
2636                        showStatusGuard = true;
2637                    } else {
2638                        // reset top margin
2639                        if (mlp.topMargin != 0) {
2640                            mlpChanged = true;
2641                            mlp.topMargin = 0;
2642                        }
2643                    }
2644                    if (mlpChanged) {
2645                        mActionModeView.setLayoutParams(mlp);
2646                    }
2647                }
2648            }
2649            if (mStatusGuard != null) {
2650                mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
2651            }
2652        }
2653
2654        private void updateNavigationGuard(Rect insets) {
2655            // IMEs lay out below the nav bar, but the content view must not (for back compat)
2656            if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
2657                // prevent the content view from including the nav bar height
2658                if (mContentParent != null) {
2659                    if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
2660                        MarginLayoutParams mlp =
2661                                (MarginLayoutParams) mContentParent.getLayoutParams();
2662                        mlp.bottomMargin = insets.bottom;
2663                        mContentParent.setLayoutParams(mlp);
2664                    }
2665                }
2666                // position the navigation guard view, creating it if necessary
2667                if (mNavigationGuard == null) {
2668                    mNavigationGuard = new View(mContext);
2669                    mNavigationGuard.setBackgroundColor(mContext.getResources()
2670                            .getColor(R.color.input_method_navigation_guard));
2671                    addView(mNavigationGuard, new LayoutParams(
2672                            LayoutParams.MATCH_PARENT, insets.bottom,
2673                            Gravity.START | Gravity.BOTTOM));
2674                } else {
2675                    LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
2676                    lp.height = insets.bottom;
2677                    mNavigationGuard.setLayoutParams(lp);
2678                }
2679            }
2680        }
2681
2682        private void drawableChanged() {
2683            if (mChanging) {
2684                return;
2685            }
2686
2687            setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top
2688                    + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right,
2689                    mFramePadding.bottom + mBackgroundPadding.bottom);
2690            requestLayout();
2691            invalidate();
2692
2693            int opacity = PixelFormat.OPAQUE;
2694
2695            // Note: if there is no background, we will assume opaque. The
2696            // common case seems to be that an application sets there to be
2697            // no background so it can draw everything itself. For that,
2698            // we would like to assume OPAQUE and let the app force it to
2699            // the slower TRANSLUCENT mode if that is really what it wants.
2700            Drawable bg = getBackground();
2701            Drawable fg = getForeground();
2702            if (bg != null) {
2703                if (fg == null) {
2704                    opacity = bg.getOpacity();
2705                } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
2706                        && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
2707                    // If the frame padding is zero, then we can be opaque
2708                    // if either the frame -or- the background is opaque.
2709                    int fop = fg.getOpacity();
2710                    int bop = bg.getOpacity();
2711                    if (false)
2712                        Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
2713                    if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
2714                        opacity = PixelFormat.OPAQUE;
2715                    } else if (fop == PixelFormat.UNKNOWN) {
2716                        opacity = bop;
2717                    } else if (bop == PixelFormat.UNKNOWN) {
2718                        opacity = fop;
2719                    } else {
2720                        opacity = Drawable.resolveOpacity(fop, bop);
2721                    }
2722                } else {
2723                    // For now we have to assume translucent if there is a
2724                    // frame with padding... there is no way to tell if the
2725                    // frame and background together will draw all pixels.
2726                    if (false)
2727                        Log.v(TAG, "Padding: " + mFramePadding);
2728                    opacity = PixelFormat.TRANSLUCENT;
2729                }
2730            }
2731
2732            if (false)
2733                Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
2734            if (false)
2735                Log.v(TAG, "Selected default opacity: " + opacity);
2736
2737            mDefaultOpacity = opacity;
2738            if (mFeatureId < 0) {
2739                setDefaultWindowFormat(opacity);
2740            }
2741        }
2742
2743        @Override
2744        public void onWindowFocusChanged(boolean hasWindowFocus) {
2745            super.onWindowFocusChanged(hasWindowFocus);
2746
2747            // If the user is chording a menu shortcut, release the chord since
2748            // this window lost focus
2749            if (!hasWindowFocus && mPanelChordingKey != 0) {
2750                closePanel(FEATURE_OPTIONS_PANEL);
2751            }
2752
2753            final Callback cb = getCallback();
2754            if (cb != null && !isDestroyed() && mFeatureId < 0) {
2755                cb.onWindowFocusChanged(hasWindowFocus);
2756            }
2757        }
2758
2759        void updateWindowResizeState() {
2760            Drawable bg = getBackground();
2761            hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity()
2762                    != PixelFormat.OPAQUE);
2763        }
2764
2765        @Override
2766        protected void onAttachedToWindow() {
2767            super.onAttachedToWindow();
2768
2769            updateWindowResizeState();
2770
2771            final Callback cb = getCallback();
2772            if (cb != null && !isDestroyed() && mFeatureId < 0) {
2773                cb.onAttachedToWindow();
2774            }
2775
2776            if (mFeatureId == -1) {
2777                /*
2778                 * The main window has been attached, try to restore any panels
2779                 * that may have been open before. This is called in cases where
2780                 * an activity is being killed for configuration change and the
2781                 * menu was open. When the activity is recreated, the menu
2782                 * should be shown again.
2783                 */
2784                openPanelsAfterRestore();
2785            }
2786        }
2787
2788        @Override
2789        protected void onDetachedFromWindow() {
2790            super.onDetachedFromWindow();
2791
2792            final Callback cb = getCallback();
2793            if (cb != null && mFeatureId < 0) {
2794                cb.onDetachedFromWindow();
2795            }
2796
2797            if (mActionBar != null) {
2798                mActionBar.dismissPopupMenus();
2799            }
2800
2801            if (mActionModePopup != null) {
2802                removeCallbacks(mShowActionModePopup);
2803                if (mActionModePopup.isShowing()) {
2804                    mActionModePopup.dismiss();
2805                }
2806                mActionModePopup = null;
2807            }
2808
2809            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
2810            if (st != null && st.menu != null && mFeatureId < 0) {
2811                st.menu.close();
2812            }
2813        }
2814
2815        @Override
2816        public void onCloseSystemDialogs(String reason) {
2817            if (mFeatureId >= 0) {
2818                closeAllPanels();
2819            }
2820        }
2821
2822        public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
2823            return mFeatureId < 0 ? mTakeSurfaceCallback : null;
2824        }
2825
2826        public InputQueue.Callback willYouTakeTheInputQueue() {
2827            return mFeatureId < 0 ? mTakeInputQueueCallback : null;
2828        }
2829
2830        public void setSurfaceType(int type) {
2831            PhoneWindow.this.setType(type);
2832        }
2833
2834        public void setSurfaceFormat(int format) {
2835            PhoneWindow.this.setFormat(format);
2836        }
2837
2838        public void setSurfaceKeepScreenOn(boolean keepOn) {
2839            if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2840            else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2841        }
2842
2843        /**
2844         * Clears out internal reference when the action mode is destroyed.
2845         */
2846        private class ActionModeCallbackWrapper implements ActionMode.Callback {
2847            private ActionMode.Callback mWrapped;
2848
2849            public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
2850                mWrapped = wrapped;
2851            }
2852
2853            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2854                return mWrapped.onCreateActionMode(mode, menu);
2855            }
2856
2857            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2858                requestFitSystemWindows();
2859                return mWrapped.onPrepareActionMode(mode, menu);
2860            }
2861
2862            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2863                return mWrapped.onActionItemClicked(mode, item);
2864            }
2865
2866            public void onDestroyActionMode(ActionMode mode) {
2867                mWrapped.onDestroyActionMode(mode);
2868                if (mActionModePopup != null) {
2869                    removeCallbacks(mShowActionModePopup);
2870                    mActionModePopup.dismiss();
2871                } else if (mActionModeView != null) {
2872                    mActionModeView.setVisibility(GONE);
2873                }
2874                if (mActionModeView != null) {
2875                    mActionModeView.removeAllViews();
2876                }
2877                if (getCallback() != null && !isDestroyed()) {
2878                    try {
2879                        getCallback().onActionModeFinished(mActionMode);
2880                    } catch (AbstractMethodError ame) {
2881                        // Older apps might not implement this callback method.
2882                    }
2883                }
2884                mActionMode = null;
2885                requestFitSystemWindows();
2886            }
2887        }
2888    }
2889
2890    protected DecorView generateDecor() {
2891        return new DecorView(getContext(), -1);
2892    }
2893
2894    protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
2895            int drawableAttr, int alphaAttr) {
2896        Drawable d = attrs.getDrawable(drawableAttr);
2897        if (d != null) {
2898            requestFeature(featureId);
2899            setFeatureDefaultDrawable(featureId, d);
2900        }
2901        if ((getFeatures() & (1 << featureId)) != 0) {
2902            int alpha = attrs.getInt(alphaAttr, -1);
2903            if (alpha >= 0) {
2904                setFeatureDrawableAlpha(featureId, alpha);
2905            }
2906        }
2907    }
2908
2909    protected ViewGroup generateLayout(DecorView decor) {
2910        // Apply data from current theme.
2911
2912        TypedArray a = getWindowStyle();
2913
2914        if (false) {
2915            System.out.println("From style:");
2916            String s = "Attrs:";
2917            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
2918                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
2919                        + a.getString(i);
2920            }
2921            System.out.println(s);
2922        }
2923
2924        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
2925        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
2926                & (~getForcedWindowFlags());
2927        if (mIsFloating) {
2928            setLayout(WRAP_CONTENT, WRAP_CONTENT);
2929            setFlags(0, flagsToUpdate);
2930        } else {
2931            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
2932        }
2933
2934        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
2935            requestFeature(FEATURE_NO_TITLE);
2936        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
2937            // Don't allow an action bar if there is no title.
2938            requestFeature(FEATURE_ACTION_BAR);
2939        }
2940
2941        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
2942            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
2943        }
2944
2945        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
2946            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
2947        }
2948
2949        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSwipeToDismiss, false)) {
2950            requestFeature(FEATURE_SWIPE_TO_DISMISS);
2951        }
2952
2953        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
2954            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
2955        }
2956
2957        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus,
2958                false)) {
2959            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
2960                    & (~getForcedWindowFlags()));
2961        }
2962
2963        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,
2964                false)) {
2965            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
2966                    & (~getForcedWindowFlags()));
2967        }
2968
2969        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {
2970            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
2971        }
2972
2973        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
2974            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
2975        }
2976
2977        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,
2978                getContext().getApplicationInfo().targetSdkVersion
2979                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
2980            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
2981        }
2982
2983        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
2984        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
2985        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {
2986            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
2987            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,
2988                    mFixedWidthMajor);
2989        }
2990        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {
2991            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
2992            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,
2993                    mFixedWidthMinor);
2994        }
2995        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {
2996            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
2997            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,
2998                    mFixedHeightMajor);
2999        }
3000        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {
3001            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
3002            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,
3003                    mFixedHeightMinor);
3004        }
3005        if (a.getBoolean(com.android.internal.R.styleable.Window_windowContentTransitions, false)) {
3006            requestFeature(FEATURE_CONTENT_TRANSITIONS);
3007        }
3008        if (a.hasValue(com.android.internal.R.styleable.Window_windowOutsetBottom)) {
3009            if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
3010            a.getValue(com.android.internal.R.styleable.Window_windowOutsetBottom, mOutsetBottom);
3011        }
3012
3013        final Context context = getContext();
3014        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
3015        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
3016        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
3017        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
3018                com.android.internal.R.bool.target_honeycomb_needs_options_menu);
3019        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
3020
3021        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
3022            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
3023        } else {
3024            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
3025        }
3026
3027        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
3028                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
3029            if (a.getBoolean(
3030                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
3031                    false)) {
3032                setCloseOnTouchOutsideIfNotSet(true);
3033            }
3034        }
3035
3036        WindowManager.LayoutParams params = getAttributes();
3037
3038        if (!hasSoftInputMode()) {
3039            params.softInputMode = a.getInt(
3040                    com.android.internal.R.styleable.Window_windowSoftInputMode,
3041                    params.softInputMode);
3042        }
3043
3044        if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
3045                mIsFloating)) {
3046            /* All dialogs should have the window dimmed */
3047            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
3048                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
3049            }
3050            if (!haveDimAmount()) {
3051                params.dimAmount = a.getFloat(
3052                        android.R.styleable.Window_backgroundDimAmount, 0.5f);
3053            }
3054        }
3055
3056        if (params.windowAnimations == 0) {
3057            params.windowAnimations = a.getResourceId(
3058                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3059        }
3060
3061        // The rest are only done if this window is not embedded; otherwise,
3062        // the values are inherited from our container.
3063        if (getContainer() == null) {
3064            if (mBackgroundDrawable == null) {
3065                if (mBackgroundResource == 0) {
3066                    mBackgroundResource = a.getResourceId(
3067                            com.android.internal.R.styleable.Window_windowBackground, 0);
3068                }
3069                if (mFrameResource == 0) {
3070                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);
3071                }
3072                if (false) {
3073                    System.out.println("Background: "
3074                            + Integer.toHexString(mBackgroundResource) + " Frame: "
3075                            + Integer.toHexString(mFrameResource));
3076                }
3077            }
3078            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
3079        }
3080
3081        // Inflate the window decor.
3082
3083        int layoutResource;
3084        int features = getLocalFeatures();
3085        // System.out.println("Features: 0x" + Integer.toHexString(features));
3086        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
3087            layoutResource = com.android.internal.R.layout.screen_swipe_dismiss;
3088        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
3089            if (mIsFloating) {
3090                TypedValue res = new TypedValue();
3091                getContext().getTheme().resolveAttribute(
3092                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
3093                layoutResource = res.resourceId;
3094            } else {
3095                layoutResource = com.android.internal.R.layout.screen_title_icons;
3096            }
3097            // XXX Remove this once action bar supports these features.
3098            removeFeature(FEATURE_ACTION_BAR);
3099            // System.out.println("Title Icons!");
3100        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
3101                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
3102            // Special case for a window with only a progress bar (and title).
3103            // XXX Need to have a no-title version of embedded windows.
3104            layoutResource = com.android.internal.R.layout.screen_progress;
3105            // System.out.println("Progress!");
3106        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
3107            // Special case for a window with a custom title.
3108            // If the window is floating, we need a dialog layout
3109            if (mIsFloating) {
3110                TypedValue res = new TypedValue();
3111                getContext().getTheme().resolveAttribute(
3112                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
3113                layoutResource = res.resourceId;
3114            } else {
3115                layoutResource = com.android.internal.R.layout.screen_custom_title;
3116            }
3117            // XXX Remove this once action bar supports these features.
3118            removeFeature(FEATURE_ACTION_BAR);
3119        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
3120            // If no other features and not embedded, only need a title.
3121            // If the window is floating, we need a dialog layout
3122            if (mIsFloating) {
3123                TypedValue res = new TypedValue();
3124                getContext().getTheme().resolveAttribute(
3125                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
3126                layoutResource = res.resourceId;
3127            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
3128                layoutResource = com.android.internal.R.layout.screen_action_bar;
3129            } else {
3130                layoutResource = com.android.internal.R.layout.screen_title;
3131            }
3132            // System.out.println("Title!");
3133        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
3134            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
3135        } else {
3136            // Embedded, so no decoration is needed.
3137            layoutResource = com.android.internal.R.layout.screen_simple;
3138            // System.out.println("Simple!");
3139        }
3140
3141        mDecor.startChanging();
3142
3143        View in = mLayoutInflater.inflate(layoutResource, null);
3144        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
3145
3146        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
3147        if (contentParent == null) {
3148            throw new RuntimeException("Window couldn't find content container view");
3149        }
3150
3151        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
3152            ProgressBar progress = getCircularProgressBar(false);
3153            if (progress != null) {
3154                progress.setIndeterminate(true);
3155            }
3156        }
3157
3158        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
3159            registerSwipeCallbacks();
3160        }
3161
3162        // Remaining setup -- of background and title -- that only applies
3163        // to top-level windows.
3164        if (getContainer() == null) {
3165            Drawable drawable = mBackgroundDrawable;
3166            if (mBackgroundResource != 0) {
3167                drawable = getContext().getDrawable(mBackgroundResource);
3168            }
3169            mDecor.setWindowBackground(drawable);
3170            drawable = null;
3171            if (mFrameResource != 0) {
3172                drawable = getContext().getDrawable(mFrameResource);
3173            }
3174            mDecor.setWindowFrame(drawable);
3175
3176            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
3177            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
3178            // " Title=" + Integer.toHexString(mTitleColor));
3179
3180            if (mTitleColor == 0) {
3181                mTitleColor = mTextColor;
3182            }
3183
3184            if (mTitle != null) {
3185                setTitle(mTitle);
3186            }
3187            setTitleColor(mTitleColor);
3188        }
3189
3190        mDecor.finishChanging();
3191
3192        return contentParent;
3193    }
3194
3195    /** @hide */
3196    public void alwaysReadCloseOnTouchAttr() {
3197        mAlwaysReadCloseOnTouchAttr = true;
3198    }
3199
3200    private void installDecor() {
3201        if (mDecor == null) {
3202            mDecor = generateDecor();
3203            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
3204            mDecor.setIsRootNamespace(true);
3205            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
3206                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
3207            }
3208        }
3209        if (mContentParent == null) {
3210            mContentParent = generateLayout(mDecor);
3211
3212            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
3213            mDecor.makeOptionalFitsSystemWindows();
3214
3215            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
3216            if (mTitleView != null) {
3217                mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
3218                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
3219                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
3220                    if (titleContainer != null) {
3221                        titleContainer.setVisibility(View.GONE);
3222                    } else {
3223                        mTitleView.setVisibility(View.GONE);
3224                    }
3225                    if (mContentParent instanceof FrameLayout) {
3226                        ((FrameLayout)mContentParent).setForeground(null);
3227                    }
3228                } else {
3229                    mTitleView.setText(mTitle);
3230                }
3231            } else {
3232                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
3233                if (mActionBar != null) {
3234                    mActionBar.setWindowCallback(getCallback());
3235                    if (mActionBar.getTitle() == null) {
3236                        mActionBar.setWindowTitle(mTitle);
3237                    }
3238                    final int localFeatures = getLocalFeatures();
3239                    if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {
3240                        mActionBar.initProgress();
3241                    }
3242                    if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
3243                        mActionBar.initIndeterminateProgress();
3244                    }
3245
3246                    final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(
3247                            com.android.internal.R.id.action_bar_overlay_layout);
3248                    if (abol != null) {
3249                        abol.setOverlayMode(
3250                                (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);
3251                    }
3252
3253                    boolean splitActionBar = false;
3254                    final boolean splitWhenNarrow =
3255                            (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
3256                    if (splitWhenNarrow) {
3257                        splitActionBar = getContext().getResources().getBoolean(
3258                                com.android.internal.R.bool.split_action_bar_is_narrow);
3259                    } else {
3260                        splitActionBar = getWindowStyle().getBoolean(
3261                                com.android.internal.R.styleable.Window_windowSplitActionBar, false);
3262                    }
3263                    final ActionBarContainer splitView = (ActionBarContainer) findViewById(
3264                            com.android.internal.R.id.split_action_bar);
3265                    if (splitView != null) {
3266                        mActionBar.setSplitView(splitView);
3267                        mActionBar.setSplitActionBar(splitActionBar);
3268                        mActionBar.setSplitWhenNarrow(splitWhenNarrow);
3269
3270                        final ActionBarContextView cab = (ActionBarContextView) findViewById(
3271                                com.android.internal.R.id.action_context_bar);
3272                        cab.setSplitView(splitView);
3273                        cab.setSplitActionBar(splitActionBar);
3274                        cab.setSplitWhenNarrow(splitWhenNarrow);
3275                    } else if (splitActionBar) {
3276                        Log.e(TAG, "Requested split action bar with " +
3277                                "incompatible window decor! Ignoring request.");
3278                    }
3279
3280                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
3281                            (mIconRes != 0 && !mActionBar.hasIcon())) {
3282                        mActionBar.setIcon(mIconRes);
3283                    } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
3284                            mIconRes == 0 && !mActionBar.hasIcon()) {
3285                        mActionBar.setIcon(
3286                                getContext().getPackageManager().getDefaultActivityIcon());
3287                        mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
3288                    }
3289                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
3290                            (mLogoRes != 0 && !mActionBar.hasLogo())) {
3291                        mActionBar.setLogo(mLogoRes);
3292                    }
3293
3294                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu
3295                    // being called in the middle of onCreate or similar.
3296                    mDecor.post(new Runnable() {
3297                        public void run() {
3298                            // Invalidate if the panel menu hasn't been created before this.
3299                            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
3300                            if (!isDestroyed() && (st == null || st.menu == null)) {
3301                                invalidatePanelMenu(FEATURE_ACTION_BAR);
3302                            }
3303                        }
3304                    });
3305                }
3306            }
3307
3308            // Only inflate or create a new TransitionManager if the caller hasn't
3309            // already set a custom one.
3310            if (hasFeature(FEATURE_CONTENT_TRANSITIONS) && mTransitionManager == null) {
3311                final int transitionRes = getWindowStyle().getResourceId(
3312                        com.android.internal.R.styleable.Window_windowContentTransitionManager, 0);
3313                if (transitionRes != 0) {
3314                    final TransitionInflater inflater = TransitionInflater.from(getContext());
3315                    mTransitionManager = inflater.inflateTransitionManager(transitionRes,
3316                            mContentParent);
3317                } else {
3318                    mTransitionManager = new TransitionManager();
3319                }
3320            }
3321        }
3322    }
3323
3324    private Drawable loadImageURI(Uri uri) {
3325        try {
3326            final Context context = getContext();
3327            return Drawable.createFromStreamThemed(
3328                    context.getContentResolver().openInputStream(uri), null, context.getTheme());
3329        } catch (Exception e) {
3330            Log.w(TAG, "Unable to open content: " + uri);
3331        }
3332        return null;
3333    }
3334
3335    private DrawableFeatureState getDrawableState(int featureId, boolean required) {
3336        if ((getFeatures() & (1 << featureId)) == 0) {
3337            if (!required) {
3338                return null;
3339            }
3340            throw new RuntimeException("The feature has not been requested");
3341        }
3342
3343        DrawableFeatureState[] ar;
3344        if ((ar = mDrawables) == null || ar.length <= featureId) {
3345            DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1];
3346            if (ar != null) {
3347                System.arraycopy(ar, 0, nar, 0, ar.length);
3348            }
3349            mDrawables = ar = nar;
3350        }
3351
3352        DrawableFeatureState st = ar[featureId];
3353        if (st == null) {
3354            ar[featureId] = st = new DrawableFeatureState(featureId);
3355        }
3356        return st;
3357    }
3358
3359    /**
3360     * Gets a panel's state based on its feature ID.
3361     *
3362     * @param featureId The feature ID of the panel.
3363     * @param required Whether the panel is required (if it is required and it
3364     *            isn't in our features, this throws an exception).
3365     * @return The panel state.
3366     */
3367    private PanelFeatureState getPanelState(int featureId, boolean required) {
3368        return getPanelState(featureId, required, null);
3369    }
3370
3371    /**
3372     * Gets a panel's state based on its feature ID.
3373     *
3374     * @param featureId The feature ID of the panel.
3375     * @param required Whether the panel is required (if it is required and it
3376     *            isn't in our features, this throws an exception).
3377     * @param convertPanelState Optional: If the panel state does not exist, use
3378     *            this as the panel state.
3379     * @return The panel state.
3380     */
3381    private PanelFeatureState getPanelState(int featureId, boolean required,
3382            PanelFeatureState convertPanelState) {
3383        if ((getFeatures() & (1 << featureId)) == 0) {
3384            if (!required) {
3385                return null;
3386            }
3387            throw new RuntimeException("The feature has not been requested");
3388        }
3389
3390        PanelFeatureState[] ar;
3391        if ((ar = mPanels) == null || ar.length <= featureId) {
3392            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
3393            if (ar != null) {
3394                System.arraycopy(ar, 0, nar, 0, ar.length);
3395            }
3396            mPanels = ar = nar;
3397        }
3398
3399        PanelFeatureState st = ar[featureId];
3400        if (st == null) {
3401            ar[featureId] = st = (convertPanelState != null)
3402                    ? convertPanelState
3403                    : new PanelFeatureState(featureId);
3404        }
3405        return st;
3406    }
3407
3408    @Override
3409    public final void setChildDrawable(int featureId, Drawable drawable) {
3410        DrawableFeatureState st = getDrawableState(featureId, true);
3411        st.child = drawable;
3412        updateDrawable(featureId, st, false);
3413    }
3414
3415    @Override
3416    public final void setChildInt(int featureId, int value) {
3417        updateInt(featureId, value, false);
3418    }
3419
3420    @Override
3421    public boolean isShortcutKey(int keyCode, KeyEvent event) {
3422        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
3423        return st.menu != null && st.menu.isShortcutKey(keyCode, event);
3424    }
3425
3426    private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {
3427        // Do nothing if the decor is not yet installed... an update will
3428        // need to be forced when we eventually become active.
3429        if (mContentParent == null) {
3430            return;
3431        }
3432
3433        final int featureMask = 1 << featureId;
3434
3435        if ((getFeatures() & featureMask) == 0 && !fromResume) {
3436            return;
3437        }
3438
3439        Drawable drawable = null;
3440        if (st != null) {
3441            drawable = st.child;
3442            if (drawable == null)
3443                drawable = st.local;
3444            if (drawable == null)
3445                drawable = st.def;
3446        }
3447        if ((getLocalFeatures() & featureMask) == 0) {
3448            if (getContainer() != null) {
3449                if (isActive() || fromResume) {
3450                    getContainer().setChildDrawable(featureId, drawable);
3451                }
3452            }
3453        } else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) {
3454            // System.out.println("Drawable changed: old=" + st.cur
3455            // + ", new=" + drawable);
3456            st.cur = drawable;
3457            st.curAlpha = st.alpha;
3458            onDrawableChanged(featureId, drawable, st.alpha);
3459        }
3460    }
3461
3462    private void updateInt(int featureId, int value, boolean fromResume) {
3463
3464        // Do nothing if the decor is not yet installed... an update will
3465        // need to be forced when we eventually become active.
3466        if (mContentParent == null) {
3467            return;
3468        }
3469
3470        final int featureMask = 1 << featureId;
3471
3472        if ((getFeatures() & featureMask) == 0 && !fromResume) {
3473            return;
3474        }
3475
3476        if ((getLocalFeatures() & featureMask) == 0) {
3477            if (getContainer() != null) {
3478                getContainer().setChildInt(featureId, value);
3479            }
3480        } else {
3481            onIntChanged(featureId, value);
3482        }
3483    }
3484
3485    private ImageView getLeftIconView() {
3486        if (mLeftIconView != null) {
3487            return mLeftIconView;
3488        }
3489        if (mContentParent == null) {
3490            installDecor();
3491        }
3492        return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon));
3493    }
3494
3495    private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
3496        if (mCircularProgressBar != null) {
3497            return mCircularProgressBar;
3498        }
3499        if (mContentParent == null && shouldInstallDecor) {
3500            installDecor();
3501        }
3502        mCircularProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_circular);
3503        if (mCircularProgressBar != null) {
3504            mCircularProgressBar.setVisibility(View.INVISIBLE);
3505        }
3506        return mCircularProgressBar;
3507    }
3508
3509    private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
3510        if (mHorizontalProgressBar != null) {
3511            return mHorizontalProgressBar;
3512        }
3513        if (mContentParent == null && shouldInstallDecor) {
3514            installDecor();
3515        }
3516        mHorizontalProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_horizontal);
3517        if (mHorizontalProgressBar != null) {
3518            mHorizontalProgressBar.setVisibility(View.INVISIBLE);
3519        }
3520        return mHorizontalProgressBar;
3521    }
3522
3523    private ImageView getRightIconView() {
3524        if (mRightIconView != null) {
3525            return mRightIconView;
3526        }
3527        if (mContentParent == null) {
3528            installDecor();
3529        }
3530        return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon));
3531    }
3532
3533    private void registerSwipeCallbacks() {
3534        SwipeDismissLayout swipeDismiss =
3535                (SwipeDismissLayout) findViewById(com.android.internal.R.id.content);
3536        swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() {
3537            @Override
3538            public void onDismissed(SwipeDismissLayout layout) {
3539                Callback cb = getCallback();
3540                if (cb != null) {
3541                    try {
3542                        cb.onWindowDismissed();
3543                    } catch (AbstractMethodError e) {
3544                        Log.e(TAG, "onWindowDismissed not implemented in " +
3545                                cb.getClass().getSimpleName(), e);
3546                    }
3547                }
3548            }
3549        });
3550        swipeDismiss.setOnSwipeProgressChangedListener(
3551                new SwipeDismissLayout.OnSwipeProgressChangedListener() {
3552                    private boolean mIsTranslucent = false;
3553
3554                    @Override
3555                    public void onSwipeProgressChanged(
3556                            SwipeDismissLayout layout, float progress, float translate) {
3557                        WindowManager.LayoutParams newParams = getAttributes();
3558                        newParams.x = (int) translate;
3559                        setAttributes(newParams);
3560
3561                        int flags = 0;
3562                        if (newParams.x == 0) {
3563                            flags = FLAG_FULLSCREEN;
3564                        } else {
3565                            flags = FLAG_LAYOUT_NO_LIMITS;
3566                        }
3567                        setFlags(flags, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
3568                    }
3569
3570                    @Override
3571                    public void onSwipeCancelled(SwipeDismissLayout layout) {
3572                        WindowManager.LayoutParams newParams = getAttributes();
3573                        newParams.x = 0;
3574                        setAttributes(newParams);
3575                        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
3576                    }
3577                });
3578    }
3579
3580    /**
3581     * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)}
3582     * callback. This method will grab whatever extra state is needed for the
3583     * callback that isn't given in the parameters. If the panel is not open,
3584     * this will not perform the callback.
3585     *
3586     * @param featureId Feature ID of the panel that was closed. Must be given.
3587     * @param panel Panel that was closed. Optional but useful if there is no
3588     *            menu given.
3589     * @param menu The menu that was closed. Optional, but give if you have.
3590     */
3591    private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
3592        final Callback cb = getCallback();
3593        if (cb == null)
3594            return;
3595
3596        // Try to get a menu
3597        if (menu == null) {
3598            // Need a panel to grab the menu, so try to get that
3599            if (panel == null) {
3600                if ((featureId >= 0) && (featureId < mPanels.length)) {
3601                    panel = mPanels[featureId];
3602                }
3603            }
3604
3605            if (panel != null) {
3606                // menu still may be null, which is okay--we tried our best
3607                menu = panel.menu;
3608            }
3609        }
3610
3611        // If the panel is not open, do not callback
3612        if ((panel != null) && (!panel.isOpen))
3613            return;
3614
3615        if (!isDestroyed()) {
3616            cb.onPanelClosed(featureId, menu);
3617        }
3618    }
3619
3620    /**
3621     * Helper method for adding launch-search to most applications. Opens the
3622     * search window using default settings.
3623     *
3624     * @return true if search window opened
3625     */
3626    private boolean launchDefaultSearch() {
3627        final Callback cb = getCallback();
3628        if (cb == null || isDestroyed()) {
3629            return false;
3630        } else {
3631            sendCloseSystemWindows("search");
3632            return cb.onSearchRequested();
3633        }
3634    }
3635
3636    @Override
3637    public void setVolumeControlStream(int streamType) {
3638        mVolumeControlStreamType = streamType;
3639    }
3640
3641    @Override
3642    public int getVolumeControlStream() {
3643        return mVolumeControlStreamType;
3644    }
3645
3646    private boolean isTranslucent() {
3647        TypedArray a = getWindowStyle();
3648        return a.getBoolean(a.getResourceId(
3649                com.android.internal.R.styleable.Window_windowIsTranslucent, 0), false);
3650    }
3651
3652    private static final class DrawableFeatureState {
3653        DrawableFeatureState(int _featureId) {
3654            featureId = _featureId;
3655        }
3656
3657        final int featureId;
3658
3659        int resid;
3660
3661        Uri uri;
3662
3663        Drawable local;
3664
3665        Drawable child;
3666
3667        Drawable def;
3668
3669        Drawable cur;
3670
3671        int alpha = 255;
3672
3673        int curAlpha = 255;
3674    }
3675
3676    private static final class PanelFeatureState {
3677
3678        /** Feature ID for this panel. */
3679        int featureId;
3680
3681        // Information pulled from the style for this panel.
3682
3683        int background;
3684
3685        /** The background when the panel spans the entire available width. */
3686        int fullBackground;
3687
3688        int gravity;
3689
3690        int x;
3691
3692        int y;
3693
3694        int windowAnimations;
3695
3696        /** Dynamic state of the panel. */
3697        DecorView decorView;
3698
3699        /** The panel that was returned by onCreatePanelView(). */
3700        View createdPanelView;
3701
3702        /** The panel that we are actually showing. */
3703        View shownPanelView;
3704
3705        /** Use {@link #setMenu} to set this. */
3706        MenuBuilder menu;
3707
3708        IconMenuPresenter iconMenuPresenter;
3709        ListMenuPresenter listMenuPresenter;
3710
3711        /** true if this menu will show in single-list compact mode */
3712        boolean isCompact;
3713
3714        /** Theme resource ID for list elements of the panel menu */
3715        int listPresenterTheme;
3716
3717        /**
3718         * Whether the panel has been prepared (see
3719         * {@link PhoneWindow#preparePanel}).
3720         */
3721        boolean isPrepared;
3722
3723        /**
3724         * Whether an item's action has been performed. This happens in obvious
3725         * scenarios (user clicks on menu item), but can also happen with
3726         * chording menu+(shortcut key).
3727         */
3728        boolean isHandled;
3729
3730        boolean isOpen;
3731
3732        /**
3733         * True if the menu is in expanded mode, false if the menu is in icon
3734         * mode
3735         */
3736        boolean isInExpandedMode;
3737
3738        public boolean qwertyMode;
3739
3740        boolean refreshDecorView;
3741
3742        boolean refreshMenuContent;
3743
3744        boolean wasLastOpen;
3745
3746        boolean wasLastExpanded;
3747
3748        /**
3749         * Contains the state of the menu when told to freeze.
3750         */
3751        Bundle frozenMenuState;
3752
3753        /**
3754         * Contains the state of associated action views when told to freeze.
3755         * These are saved across invalidations.
3756         */
3757        Bundle frozenActionViewState;
3758
3759        PanelFeatureState(int featureId) {
3760            this.featureId = featureId;
3761
3762            refreshDecorView = false;
3763        }
3764
3765        public boolean isInListMode() {
3766            return isInExpandedMode || isCompact;
3767        }
3768
3769        public boolean hasPanelItems() {
3770            if (shownPanelView == null) return false;
3771            if (createdPanelView != null) return true;
3772
3773            if (isCompact || isInExpandedMode) {
3774                return listMenuPresenter.getAdapter().getCount() > 0;
3775            } else {
3776                return ((ViewGroup) shownPanelView).getChildCount() > 0;
3777            }
3778        }
3779
3780        /**
3781         * Unregister and free attached MenuPresenters. They will be recreated as needed.
3782         */
3783        public void clearMenuPresenters() {
3784            if (menu != null) {
3785                menu.removeMenuPresenter(iconMenuPresenter);
3786                menu.removeMenuPresenter(listMenuPresenter);
3787            }
3788            iconMenuPresenter = null;
3789            listMenuPresenter = null;
3790        }
3791
3792        void setStyle(Context context) {
3793            TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
3794            background = a.getResourceId(
3795                    com.android.internal.R.styleable.Theme_panelBackground, 0);
3796            fullBackground = a.getResourceId(
3797                    com.android.internal.R.styleable.Theme_panelFullBackground, 0);
3798            windowAnimations = a.getResourceId(
3799                    com.android.internal.R.styleable.Theme_windowAnimationStyle, 0);
3800            isCompact = a.getBoolean(
3801                    com.android.internal.R.styleable.Theme_panelMenuIsCompact, false);
3802            listPresenterTheme = a.getResourceId(
3803                    com.android.internal.R.styleable.Theme_panelMenuListTheme,
3804                    com.android.internal.R.style.Theme_ExpandedMenu);
3805            a.recycle();
3806        }
3807
3808        void setMenu(MenuBuilder menu) {
3809            if (menu == this.menu) return;
3810
3811            if (this.menu != null) {
3812                this.menu.removeMenuPresenter(iconMenuPresenter);
3813                this.menu.removeMenuPresenter(listMenuPresenter);
3814            }
3815            this.menu = menu;
3816            if (menu != null) {
3817                if (iconMenuPresenter != null) menu.addMenuPresenter(iconMenuPresenter);
3818                if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
3819            }
3820        }
3821
3822        MenuView getListMenuView(Context context, MenuPresenter.Callback cb) {
3823            if (menu == null) return null;
3824
3825            if (!isCompact) {
3826                getIconMenuView(context, cb); // Need this initialized to know where our offset goes
3827            }
3828
3829            if (listMenuPresenter == null) {
3830                listMenuPresenter = new ListMenuPresenter(
3831                        com.android.internal.R.layout.list_menu_item_layout, listPresenterTheme);
3832                listMenuPresenter.setCallback(cb);
3833                listMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter);
3834                menu.addMenuPresenter(listMenuPresenter);
3835            }
3836
3837            if (iconMenuPresenter != null) {
3838                listMenuPresenter.setItemIndexOffset(
3839                        iconMenuPresenter.getNumActualItemsShown());
3840            }
3841            MenuView result = listMenuPresenter.getMenuView(decorView);
3842
3843            return result;
3844        }
3845
3846        MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) {
3847            if (menu == null) return null;
3848
3849            if (iconMenuPresenter == null) {
3850                iconMenuPresenter = new IconMenuPresenter(context);
3851                iconMenuPresenter.setCallback(cb);
3852                iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter);
3853                menu.addMenuPresenter(iconMenuPresenter);
3854            }
3855
3856            MenuView result = iconMenuPresenter.getMenuView(decorView);
3857
3858            return result;
3859        }
3860
3861        Parcelable onSaveInstanceState() {
3862            SavedState savedState = new SavedState();
3863            savedState.featureId = featureId;
3864            savedState.isOpen = isOpen;
3865            savedState.isInExpandedMode = isInExpandedMode;
3866
3867            if (menu != null) {
3868                savedState.menuState = new Bundle();
3869                menu.savePresenterStates(savedState.menuState);
3870            }
3871
3872            return savedState;
3873        }
3874
3875        void onRestoreInstanceState(Parcelable state) {
3876            SavedState savedState = (SavedState) state;
3877            featureId = savedState.featureId;
3878            wasLastOpen = savedState.isOpen;
3879            wasLastExpanded = savedState.isInExpandedMode;
3880            frozenMenuState = savedState.menuState;
3881
3882            /*
3883             * A LocalActivityManager keeps the same instance of this class around.
3884             * The first time the menu is being shown after restoring, the
3885             * Activity.onCreateOptionsMenu should be called. But, if it is the
3886             * same instance then menu != null and we won't call that method.
3887             * We clear any cached views here. The caller should invalidatePanelMenu.
3888             */
3889            createdPanelView = null;
3890            shownPanelView = null;
3891            decorView = null;
3892        }
3893
3894        void applyFrozenState() {
3895            if (menu != null && frozenMenuState != null) {
3896                menu.restorePresenterStates(frozenMenuState);
3897                frozenMenuState = null;
3898            }
3899        }
3900
3901        private static class SavedState implements Parcelable {
3902            int featureId;
3903            boolean isOpen;
3904            boolean isInExpandedMode;
3905            Bundle menuState;
3906
3907            public int describeContents() {
3908                return 0;
3909            }
3910
3911            public void writeToParcel(Parcel dest, int flags) {
3912                dest.writeInt(featureId);
3913                dest.writeInt(isOpen ? 1 : 0);
3914                dest.writeInt(isInExpandedMode ? 1 : 0);
3915
3916                if (isOpen) {
3917                    dest.writeBundle(menuState);
3918                }
3919            }
3920
3921            private static SavedState readFromParcel(Parcel source) {
3922                SavedState savedState = new SavedState();
3923                savedState.featureId = source.readInt();
3924                savedState.isOpen = source.readInt() == 1;
3925                savedState.isInExpandedMode = source.readInt() == 1;
3926
3927                if (savedState.isOpen) {
3928                    savedState.menuState = source.readBundle();
3929                }
3930
3931                return savedState;
3932            }
3933
3934            public static final Parcelable.Creator<SavedState> CREATOR
3935                    = new Parcelable.Creator<SavedState>() {
3936                public SavedState createFromParcel(Parcel in) {
3937                    return readFromParcel(in);
3938                }
3939
3940                public SavedState[] newArray(int size) {
3941                    return new SavedState[size];
3942                }
3943            };
3944        }
3945
3946    }
3947
3948    static class RotationWatcher extends IRotationWatcher.Stub {
3949        private Handler mHandler;
3950        private final Runnable mRotationChanged = new Runnable() {
3951            public void run() {
3952                dispatchRotationChanged();
3953            }
3954        };
3955        private final ArrayList<WeakReference<PhoneWindow>> mWindows =
3956                new ArrayList<WeakReference<PhoneWindow>>();
3957        private boolean mIsWatching;
3958
3959        @Override
3960        public void onRotationChanged(int rotation) throws RemoteException {
3961            mHandler.post(mRotationChanged);
3962        }
3963
3964        public void addWindow(PhoneWindow phoneWindow) {
3965            synchronized (mWindows) {
3966                if (!mIsWatching) {
3967                    try {
3968                        WindowManagerHolder.sWindowManager.watchRotation(this);
3969                        mHandler = new Handler();
3970                        mIsWatching = true;
3971                    } catch (RemoteException ex) {
3972                        Log.e(TAG, "Couldn't start watching for device rotation", ex);
3973                    }
3974                }
3975                mWindows.add(new WeakReference<PhoneWindow>(phoneWindow));
3976            }
3977        }
3978
3979        public void removeWindow(PhoneWindow phoneWindow) {
3980            synchronized (mWindows) {
3981                int i = 0;
3982                while (i < mWindows.size()) {
3983                    final WeakReference<PhoneWindow> ref = mWindows.get(i);
3984                    final PhoneWindow win = ref.get();
3985                    if (win == null || win == phoneWindow) {
3986                        mWindows.remove(i);
3987                    } else {
3988                        i++;
3989                    }
3990                }
3991            }
3992        }
3993
3994        void dispatchRotationChanged() {
3995            synchronized (mWindows) {
3996                int i = 0;
3997                while (i < mWindows.size()) {
3998                    final WeakReference<PhoneWindow> ref = mWindows.get(i);
3999                    final PhoneWindow win = ref.get();
4000                    if (win != null) {
4001                        win.onOptionsPanelRotationChanged();
4002                        i++;
4003                    } else {
4004                        mWindows.remove(i);
4005                    }
4006                }
4007            }
4008        }
4009    }
4010
4011    /**
4012     * Simple implementation of MenuBuilder.Callback that:
4013     * <li> Opens a submenu when selected.
4014     * <li> Calls back to the callback's onMenuItemSelected when an item is
4015     * selected.
4016     */
4017    private final class DialogMenuCallback implements MenuBuilder.Callback, MenuPresenter.Callback {
4018        private int mFeatureId;
4019        private MenuDialogHelper mSubMenuHelper;
4020
4021        public DialogMenuCallback(int featureId) {
4022            mFeatureId = featureId;
4023        }
4024
4025        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
4026            if (menu.getRootMenu() != menu) {
4027                onCloseSubMenu(menu);
4028            }
4029
4030            if (allMenusAreClosing) {
4031                Callback callback = getCallback();
4032                if (callback != null && !isDestroyed()) {
4033                    callback.onPanelClosed(mFeatureId, menu);
4034                }
4035
4036                if (menu == mContextMenu) {
4037                    dismissContextMenu();
4038                }
4039
4040                // Dismiss the submenu, if it is showing
4041                if (mSubMenuHelper != null) {
4042                    mSubMenuHelper.dismiss();
4043                    mSubMenuHelper = null;
4044                }
4045            }
4046        }
4047
4048        public void onCloseSubMenu(MenuBuilder menu) {
4049            Callback callback = getCallback();
4050            if (callback != null && !isDestroyed()) {
4051                callback.onPanelClosed(mFeatureId, menu.getRootMenu());
4052            }
4053        }
4054
4055        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
4056            Callback callback = getCallback();
4057            return (callback != null && !isDestroyed())
4058                    && callback.onMenuItemSelected(mFeatureId, item);
4059        }
4060
4061        public void onMenuModeChange(MenuBuilder menu) {
4062        }
4063
4064        public boolean onOpenSubMenu(MenuBuilder subMenu) {
4065            if (subMenu == null) return false;
4066
4067            // Set a simple callback for the submenu
4068            subMenu.setCallback(this);
4069
4070            // The window manager will give us a valid window token
4071            mSubMenuHelper = new MenuDialogHelper(subMenu);
4072            mSubMenuHelper.show(null);
4073
4074            return true;
4075        }
4076    }
4077
4078    void sendCloseSystemWindows() {
4079        PhoneWindowManager.sendCloseSystemWindows(getContext(), null);
4080    }
4081
4082    void sendCloseSystemWindows(String reason) {
4083        PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
4084    }
4085
4086    @Override
4087    public void setTransitionOptions(Bundle options, SceneTransitionListener listener) {
4088        mSceneTransitionListener = listener;
4089        ActivityOptions activityOptions = null;
4090        if (options != null) {
4091            activityOptions = new ActivityOptions(options);
4092            if (activityOptions.getAnimationType() != ActivityOptions.ANIM_SCENE_TRANSITION) {
4093                activityOptions = null;
4094            }
4095        }
4096        mActivityOptions = activityOptions;
4097    }
4098
4099    @Override
4100    public void setAllowOverlappingEnterTransition(boolean allow) {
4101        mAllowEnterOverlap = allow;
4102    }
4103
4104    @Override
4105    public void setAllowOverlappingExitTransition(boolean allow) {
4106        mAllowExitOverlap = allow;
4107    }
4108
4109    @Override
4110    public void mapTransitionTargets(Map<String, String> sharedElementNames) {
4111        mSharedElementsMap = sharedElementNames;
4112    }
4113
4114    @Override
4115    public void restoreViewVisibilityAfterTransitionToCallee() {
4116        if (mTransitioningViews != null) {
4117            setViewVisibility(mTransitioningViews, View.VISIBLE);
4118        }
4119    }
4120
4121    @Override
4122    public void startExitTransitionToCaller(final Runnable onTransitionEnd) {
4123        Transition transition;
4124        if (mContentScene == null || mTransitionManager == null || mActivityOptions == null
4125                || (transition = mTransitionManager.getEnterTransition(mContentScene)) == null) {
4126            onTransitionEnd.run();
4127            return;
4128        }
4129        if (mAllowExitOverlap) {
4130            TransitionSet transitionSet = new TransitionSet();
4131            transitionSet.addTransition(transition);
4132            Fade fade = new Fade();
4133            transitionSet.addTransition(fade);
4134            transition = transitionSet;
4135        }
4136
4137        final ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
4138        mapSharedElements(sharedElements);
4139        final Bundle sharedElementArgs = new Bundle();
4140        captureTerminalSharedElementState(sharedElements, sharedElementArgs);
4141
4142        final ArrayList<View> transitioningViews = new ArrayList<View>();
4143        mDecor.captureTransitioningViews(transitioningViews);
4144        transitioningViews.removeAll(sharedElements.values());
4145
4146        mSceneTransitionListener.convertToTranslucent();
4147        transition = transition.clone();
4148        Rect epicenter = calcEpicenter(sharedElements, mActivityOptions.getSharedElementNames());
4149        transition.setEpicenterCallback(new FixedEpicenterCallback(epicenter));
4150        ExitSceneBack exitScene =
4151                new ExitSceneBack(onTransitionEnd, sharedElementArgs, sharedElements.values());
4152        exitScene.start(transition);
4153        mTransitionManager.beginDelayedTransition(mDecor, transition);
4154        setViewVisibility(transitioningViews, View.INVISIBLE);
4155    }
4156
4157    @Override
4158    public Bundle startExitTransitionToCallee(Bundle options) {
4159        if (mContentScene == null) {
4160            return null;
4161        }
4162        Transition transition = mTransitionManager.getExitTransition(mContentScene);
4163        if (transition == null) {
4164            return null;
4165        }
4166
4167        ActivityOptions activityOptions = new ActivityOptions(options);
4168        ArrayMap<String, View> sharedElements = findSharedElements(activityOptions);
4169
4170        // Find exiting Views and shared elements
4171        ArrayList<View> transitioningViews = captureTransitioningViews(sharedElements.values());
4172
4173        Transition exitTransition = addTransitionTargets(transition,
4174                transitioningViews, true);
4175        Transition sharedElementTransition = addTransitionTargets(transition,
4176                transitioningViews, false);
4177
4178        // transitionSet is the total exit transition, including hero animation.
4179        TransitionSet transitionSet = new TransitionSet();
4180        transitionSet.addTransition(exitTransition);
4181        transitionSet.addTransition(sharedElementTransition);
4182
4183        Rect epicenter = calcEpicenter(sharedElements, activityOptions.getSharedElementNames());
4184        FixedEpicenterCallback epicenterCallback = new FixedEpicenterCallback(epicenter);
4185        transitionSet.setEpicenterCallback(epicenterCallback);
4186
4187        updateExitActivityOptions(activityOptions, sharedElements,
4188                sharedElementTransition, transitioningViews, exitTransition, epicenterCallback);
4189
4190        // Start exiting the Views that need to exit
4191        TransitionManager.beginDelayedTransition(mDecor, transitionSet);
4192        setViewVisibility(transitioningViews, View.INVISIBLE);
4193
4194        return activityOptions.toBundle();
4195    }
4196
4197    private ArrayList<View> captureTransitioningViews(Collection<View> sharedElements) {
4198        mTransitioningViews = new ArrayList<View>();
4199        mDecor.captureTransitioningViews(mTransitioningViews);
4200        ArrayList<View> transitioningViews = (ArrayList<View>) mTransitioningViews.clone();
4201        transitioningViews.removeAll(sharedElements);
4202        return transitioningViews;
4203    }
4204
4205    private ArrayMap<String, View> findSharedElements(ActivityOptions activityOptions) {
4206        ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
4207        mDecor.findSharedElements(sharedElements);
4208        ArrayList<String> localNames = activityOptions.getLocalElementNames();
4209        sharedElements.keySet().retainAll(localNames);
4210
4211        ArrayList<String> targetNames = activityOptions.getSharedElementNames();
4212        for (int i = 0; i < localNames.size(); i++) {
4213            String localName = localNames.get(i);
4214            View sharedElement = sharedElements.remove(localName);
4215            String targetName = targetNames.get(i);
4216            sharedElements.put(targetName, sharedElement);
4217        }
4218        return sharedElements;
4219    }
4220
4221    private static void runOnUiThread(Handler handler, Runnable runnable) {
4222        if (handler.getLooper() != Looper.myLooper()) {
4223            handler.post(runnable);
4224        } else {
4225            runnable.run();
4226        }
4227    }
4228
4229    private void updateExitActivityOptions(ActivityOptions activityOptions,
4230            final Map<String, View> sharedElements, Transition sharedElementTransition,
4231            final ArrayList<View> transitioningViews, Transition exitTransition,
4232            final Transition.EpicenterCallback epicenterCallback) {
4233
4234        // Schedule capturing of the shared element state
4235        final Bundle sharedElementArgs = new Bundle();
4236        captureTerminalSharedElementState(sharedElements, sharedElementArgs);
4237
4238        ActivityOptions.SharedElementSource sharedElementSource
4239                = new ActivityOptions.SharedElementSource() {
4240            private Handler mHandler = new Handler();
4241
4242            @Override
4243            public Bundle getSharedElementExitState() {
4244                return sharedElementArgs;
4245            }
4246
4247            @Override
4248            public void acceptedSharedElements(final ArrayList<String> sharedElementNames) {
4249                if (sharedElementNames.size() == sharedElements.size()) {
4250                    return; // They were all accepted
4251                }
4252                runOnUiThread(mHandler, new Runnable() {
4253                    @Override
4254                    public void run() {
4255                        Transition transition = mTransitionManager.getExitTransition(mContentScene);
4256                        transition = transition.clone();
4257                        transition.setEpicenterCallback(epicenterCallback);
4258                        TransitionManager.beginDelayedTransition(mDecor, transition);
4259                        for (String name : sharedElements.keySet()) {
4260                            if (!sharedElementNames.contains(name)) {
4261                                sharedElements.get(name).setVisibility(View.INVISIBLE);
4262                            }
4263                        }
4264                        sharedElements.keySet().retainAll(sharedElementNames);
4265                    }
4266                });
4267            }
4268
4269            @Override
4270            public void hideSharedElements() {
4271                if (sharedElements != null) {
4272                    runOnUiThread(mHandler, new Runnable() {
4273                        @Override
4274                        public void run() {
4275                            setViewVisibility(sharedElements.values(), View.INVISIBLE);
4276                        }
4277                    });
4278                }
4279            }
4280
4281            @Override
4282            public void restore(final Bundle sharedElementState) {
4283                runOnUiThread(mHandler, new Runnable() {
4284                    @Override
4285                    public void run() {
4286                        mTransitioningViews = null;
4287                        Transition transition = mTransitionManager.getExitTransition(mContentScene);
4288                        transition = transition.clone();
4289                        transition.setEpicenterCallback(epicenterCallback);
4290                        setSharedElementState(sharedElements, sharedElementState);
4291                        setViewVisibility(sharedElements.values(), View.VISIBLE);
4292                        if (mSceneTransitionListener != null) {
4293                            mSceneTransitionListener.sharedElementStart(transition);
4294                            mDecor.getViewTreeObserver().addOnPreDrawListener(
4295                                    new ViewTreeObserver.OnPreDrawListener() {
4296                                        @Override
4297                                        public boolean onPreDraw() {
4298                                            mDecor.getViewTreeObserver().removeOnPreDrawListener(this);
4299                                            mSceneTransitionListener.sharedElementEnd();
4300                                            return true;
4301                                        }
4302                                    });
4303                        }
4304                        TransitionManager.beginDelayedTransition(mDecor, transition);
4305                        setViewVisibility(transitioningViews, View.VISIBLE);
4306                        for (View sharedElement: sharedElements.values()) {
4307                            sharedElement.requestLayout();
4308                        }
4309                    }
4310                });
4311            }
4312
4313            @Override
4314            public void prepareForRestore() {
4315                if (mTransitioningViews != null) {
4316                    runOnUiThread(mHandler, new Runnable() {
4317                        @Override
4318                        public void run() {
4319                            setViewVisibility(mTransitioningViews, View.INVISIBLE);
4320                        }
4321                    });
4322                }
4323            }
4324        };
4325
4326        activityOptions.updateSceneTransitionAnimation(
4327                exitTransition, sharedElementTransition, sharedElementSource);
4328    }
4329
4330    private void captureTerminalSharedElementState(final Map<String, View> sharedElements,
4331            final Bundle sharedElementArgs) {
4332        mDecor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
4333            @Override
4334            public boolean onPreDraw() {
4335                mDecor.getViewTreeObserver().removeOnPreDrawListener(this);
4336                int[] tempLoc = new int[2];
4337                for (String name : sharedElements.keySet()) {
4338                    View sharedElement = sharedElements.get(name);
4339                    captureSharedElementState(sharedElement, name, sharedElementArgs, tempLoc);
4340                }
4341                return true;
4342            }
4343        });
4344    }
4345
4346    private static Transition addTransitionTargets(Transition transition, Collection<View> views,
4347            boolean add) {
4348        TransitionSet set = new TransitionSet();
4349        set.addTransition(transition.clone());
4350        for (View view: views) {
4351            if (add) {
4352                set.addTarget(view);
4353            } else {
4354                set.excludeTarget(view, true);
4355            }
4356        }
4357        return set;
4358    }
4359
4360    private static void setViewVisibility(Collection<View> views, int visibility) {
4361        for (View view : views) {
4362            view.setVisibility(visibility);
4363        }
4364    }
4365
4366    private static void setSharedElementState(Map<String, View> sharedElements,
4367            Bundle sharedElementState) {
4368        int[] tempLoc = new int[2];
4369        for (Map.Entry<String, View> entry: sharedElements.entrySet()) {
4370            setSharedElementState(entry.getValue(), entry.getKey(), sharedElementState, tempLoc);
4371        }
4372    }
4373
4374    /**
4375     * Sets the captured values from a previous
4376     * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])}
4377     * @param view The View to apply placement changes to.
4378     * @param name The shared element name given from the source Activity.
4379     * @param transitionArgs A <code>Bundle</code> containing all placementinformation for named
4380     *                       shared elements in the scene.
4381     * @param tempLoc A temporary int[2] for capturing the current location of views.
4382     */
4383    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
4384            int[] tempLoc) {
4385        Bundle sharedElementBundle = transitionArgs.getBundle(name);
4386        if (sharedElementBundle == null) {
4387            return;
4388        }
4389
4390        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
4391        view.setTranslationZ(z);
4392
4393        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
4394        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
4395        int width = sharedElementBundle.getInt(KEY_WIDTH);
4396        int height = sharedElementBundle.getInt(KEY_HEIGHT);
4397
4398        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
4399        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
4400        view.measure(widthSpec, heightSpec);
4401
4402        ViewGroup parent = (ViewGroup) view.getParent();
4403        parent.getLocationOnScreen(tempLoc);
4404        int left = x - tempLoc[0];
4405        int top = y - tempLoc[1];
4406        int right = left + width;
4407        int bottom = top + height;
4408        view.layout(left, top, right, bottom);
4409
4410        view.requestLayout();
4411    }
4412
4413    /**
4414     * Captures placement information for Views with a shared element name for
4415     * Activity Transitions.
4416     * @param view The View to capture the placement information for.
4417     * @param name The shared element name in the target Activity to apply the placement
4418     *             information for.
4419     * @param transitionArgs Bundle to store shared element placement information.
4420     * @param tempLoc A temporary int[2] for capturing the current location of views.
4421     * @see #setSharedElementState(android.view.View, String, android.os.Bundle, int[])
4422     */
4423    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
4424            int[] tempLoc) {
4425        Bundle sharedElementBundle = new Bundle();
4426        view.getLocationOnScreen(tempLoc);
4427        float scaleX = view.getScaleX();
4428        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
4429        int width = Math.round(view.getWidth() * scaleX);
4430        sharedElementBundle.putInt(KEY_WIDTH, width);
4431
4432        float scaleY = view.getScaleY();
4433        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
4434        int height= Math.round(view.getHeight() * scaleY);
4435        sharedElementBundle.putInt(KEY_HEIGHT, height);
4436
4437        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
4438
4439        sharedElementBundle.putString(KEY_NAME, view.getSharedElementName());
4440
4441        transitionArgs.putBundle(name, sharedElementBundle);
4442    }
4443
4444    private void mapSharedElements(ArrayMap<String, View> sharedElements) {
4445        ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames();
4446        if (sharedElementNames != null) {
4447            mDecor.findSharedElements(sharedElements);
4448            if (mSharedElementsMap != null) {
4449                for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) {
4450                    View sharedElement = sharedElements.remove(entry.getValue());
4451                    if (sharedElement != null) {
4452                        sharedElements.put(entry.getKey(), sharedElement);
4453                    }
4454                }
4455            }
4456            sharedElements.keySet().retainAll(sharedElementNames);
4457        }
4458    }
4459
4460    private static Rect calcEpicenter(ArrayMap<String, View> sharedElements,
4461            ArrayList<String> sharedElementNames) {
4462        if (sharedElementNames != null) {
4463            for (String name: sharedElementNames) {
4464                if (name.startsWith("android:")) {
4465                    return null;
4466                }
4467                View view = sharedElements.get(name);
4468                if (view != null) {
4469                    int[] loc = new int[2];
4470                    view.getLocationOnScreen(loc);
4471                    int left = loc[0] + Math.round(view.getTranslationX());
4472                    int top = loc[1] + Math.round(view.getTranslationY());
4473                    int right = left + view.getWidth();
4474                    int bottom = top + view.getHeight();
4475                    return new Rect(left, top, right, bottom);
4476                }
4477            }
4478        }
4479        return null;
4480    }
4481
4482    private class ExitSceneBack extends Transition.TransitionListenerAdapter implements
4483            Animator.AnimatorListener {
4484        private boolean mExitTransitionComplete;
4485        private boolean mBackgroundFadeComplete;
4486        private boolean mOnCompleteExecuted;
4487        private boolean mSharedElementTransitioned;
4488        private Runnable mOnComplete;
4489        private Bundle mSharedElementArgs;
4490        private Collection<View> mSharedElements;
4491
4492        public ExitSceneBack(Runnable onComplete, Bundle sharedElementArgs,
4493                Collection<View> sharedElements) {
4494            mOnComplete = onComplete;
4495            mSharedElementArgs = sharedElementArgs;
4496            mSharedElements = sharedElements;
4497        }
4498
4499        public void start(Transition exitTransition) {
4500            if (mActivityOptions != null) {
4501                mActivityOptions.dispatchPrepareRestore();
4502            }
4503            exitTransition.addListener(this);
4504            Drawable background = mDecor.getBackground();
4505            if (background != null) {
4506                ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 0);
4507                animator.addListener(this);
4508                animator.start();
4509            } else {
4510                mBackgroundFadeComplete = true;
4511                startCalledActivityEnter();
4512            }
4513        }
4514
4515        @Override
4516        public void onTransitionEnd(Transition transition) {
4517            transition.removeListener(this);
4518            mExitTransitionComplete = true;
4519            notifyComplete();
4520            if (!mAllowExitOverlap) {
4521                startCalledActivityEnter();
4522            }
4523        }
4524
4525        private void notifyComplete() {
4526            if (mExitTransitionComplete && mBackgroundFadeComplete
4527                    && mSharedElementTransitioned && !mOnCompleteExecuted) {
4528                mOnComplete.run();
4529                mSceneTransitionListener.nullPendingTransition();
4530                mOnCompleteExecuted = true;
4531            }
4532        }
4533
4534        @Override
4535        public void onAnimationStart(Animator animation) {
4536        }
4537
4538        @Override
4539        public void onAnimationEnd(Animator animation) {
4540            mBackgroundFadeComplete = true;
4541            if (mAllowExitOverlap) {
4542                startCalledActivityEnter();
4543            }
4544        }
4545
4546        @Override
4547        public void onAnimationCancel(Animator animation) {
4548        }
4549
4550        @Override
4551        public void onAnimationRepeat(Animator animation) {
4552        }
4553
4554        private void startCalledActivityEnter() {
4555            mActivityOptions.dispatchRestore(mSharedElementArgs);
4556            setViewVisibility(mSharedElements, View.INVISIBLE);
4557            mSharedElementTransitioned = true;
4558            notifyComplete();
4559        }
4560    }
4561
4562    /**
4563     * Provides code for handling the Activity transition entering scene.
4564     * When the first scene is laid out (onPreDraw), it makes views invisible.
4565     * It then starts the entering transition by making non-shared elements visible. When
4566     * the entering transition is started, the calling Activity is notified that
4567     * this Activity is ready to receive the shared element. When the calling Activity notifies
4568     * that the shared element is ready, this Activity is notified through the
4569     * SceneTransitionListener.
4570     *
4571     * This class also takes into account fading the background -- either waiting until the
4572     * shared element is ready or the calling Activity's exit transition is complete.
4573     */
4574    private class EnterScene implements ViewTreeObserver.OnPreDrawListener, Runnable,
4575            ActivityOptions.ActivityTransitionTarget, Animator.AnimatorListener {
4576        private boolean mSharedElementReadyReceived;
4577        private boolean mAllDone;
4578        private Handler mHandler = new Handler();
4579        private boolean mEnterTransitionStarted;
4580        private ArrayMap<String, View> mSharedElementTargets = new ArrayMap<String, View>();
4581        private ArrayList<View> mEnteringViews = new ArrayList<View>();
4582        private Transition.EpicenterCallback mEpicenterCallback;
4583
4584        public EnterScene() {
4585            mSceneTransitionListener.nullPendingTransition();
4586            Drawable background = getDecorView().getBackground();
4587            if (background != null) {
4588                background.setAlpha(0);
4589                mDecor.drawableChanged();
4590            }
4591            mSceneTransitionListener.convertToTranslucent();
4592        }
4593
4594        @Override
4595        public boolean onPreDraw() {
4596            ViewTreeObserver observer = mDecor.getViewTreeObserver();
4597            observer.removeOnPreDrawListener(this);
4598            if (!mEnterTransitionStarted && mSceneTransitionListener != null) {
4599                mEnterTransitionStarted = true;
4600                mDecor.captureTransitioningViews(mEnteringViews);
4601                mapSharedElements(mSharedElementTargets);
4602                mEnteringViews.removeAll(mSharedElementTargets.values());
4603                Rect epicenter = calcEpicenter(mSharedElementTargets,
4604                        mActivityOptions.getSharedElementNames());
4605                mEpicenterCallback = new FixedEpicenterCallback(epicenter);
4606
4607                setViewVisibility(mEnteringViews, View.INVISIBLE);
4608                setViewVisibility(mSharedElementTargets.values(), View.INVISIBLE);
4609                if (mAllowEnterOverlap) {
4610                    beginEnterScene();
4611                }
4612                observer.addOnPreDrawListener(this);
4613                return false;
4614            } else {
4615                mHandler.postDelayed(this, MAX_TRANSITION_START_WAIT);
4616                mActivityOptions.dispatchSceneTransitionStarted(this,
4617                        new ArrayList<String>(mSharedElementTargets.keySet()));
4618                return !mSharedElementReadyReceived;
4619            }
4620        }
4621
4622        public void start() {
4623            ViewTreeObserver observer = mDecor.getViewTreeObserver();
4624            observer.addOnPreDrawListener(this);
4625        }
4626
4627        @Override
4628        public void run() {
4629            exitTransitionComplete();
4630        }
4631
4632        @Override
4633        public void sharedElementTransitionComplete(final Bundle transitionArgs) {
4634            if (!mSharedElementReadyReceived) {
4635                mSharedElementReadyReceived = true;
4636                mHandler.removeCallbacks(this);
4637                mHandler.postDelayed(this, MAX_TRANSITION_FINISH_WAIT);
4638                if (!mSharedElementTargets.isEmpty()) {
4639                    runOnUiThread(mHandler, new Runnable() {
4640                        @Override
4641                        public void run() {
4642                            Transition transition = getTransitionManager().getEnterTransition(
4643                                    mContentScene);
4644                            if (transition == null) {
4645                                transition = TransitionManager.getDefaultTransition();
4646                            }
4647                            transition = addTransitionTargets(transition,
4648                                    mSharedElementTargets.values(),
4649                                    true);
4650                            transition.setEpicenterCallback(mEpicenterCallback);
4651                            if (transitionArgs == null) {
4652                                TransitionManager.beginDelayedTransition(mDecor, transition);
4653                                setViewVisibility(mSharedElementTargets.values(), View.VISIBLE);
4654                            } else {
4655                                mSceneTransitionListener.sharedElementStart(transition);
4656                                setSharedElementState(mSharedElementTargets, transitionArgs);
4657                                setViewVisibility(mSharedElementTargets.values(), View.VISIBLE);
4658                                mDecor.getViewTreeObserver().addOnPreDrawListener(
4659                                        new ViewTreeObserver.OnPreDrawListener() {
4660                                            @Override
4661                                            public boolean onPreDraw() {
4662                                                mDecor.getViewTreeObserver()
4663                                                        .removeOnPreDrawListener(this);
4664                                                mSceneTransitionListener.sharedElementEnd();
4665                                                mActivityOptions.dispatchSharedElementsReady();
4666                                                return true;
4667                                            }
4668                                        });
4669                                TransitionManager.beginDelayedTransition(mDecor, transition);
4670                            }
4671                        }
4672                    });
4673                }
4674                if (mAllowEnterOverlap) {
4675                    fadeInBackground();
4676                }
4677            }
4678        }
4679
4680        private void fadeInBackground() {
4681            Drawable background = getDecorView().getBackground();
4682            if (background == null) {
4683                mSceneTransitionListener.convertFromTranslucent();
4684            } else {
4685                ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255);
4686                animator.addListener(this);
4687                animator.start();
4688            }
4689        }
4690
4691        @Override
4692        public void exitTransitionComplete() {
4693            if (mAllDone) {
4694                return;
4695            }
4696            mAllDone = true;
4697            sharedElementTransitionComplete(null);
4698            mHandler.removeCallbacks(this);
4699            if (!mAllowEnterOverlap) {
4700                runOnUiThread(mHandler, new Runnable() {
4701                    @Override
4702                    public void run() {
4703                        beginEnterScene();
4704                        fadeInBackground();
4705                    }
4706                });
4707            }
4708        }
4709
4710        @Override
4711        public void onAnimationStart(Animator animation) {
4712        }
4713
4714        @Override
4715        public void onAnimationEnd(Animator animation) {
4716            mSceneTransitionListener.convertFromTranslucent();
4717        }
4718
4719        @Override
4720        public void onAnimationCancel(Animator animation) {
4721        }
4722
4723        @Override
4724        public void onAnimationRepeat(Animator animation) {
4725        }
4726
4727        private void beginEnterScene() {
4728            Transition transition = getTransitionManager().getEnterTransition(mContentScene);
4729            if (transition == null) {
4730                transition = TransitionManager.getDefaultTransition();
4731            }
4732            transition = addTransitionTargets(transition, mEnteringViews, true);
4733            transition.setEpicenterCallback(mEpicenterCallback);
4734            TransitionManager.beginDelayedTransition(mDecor, transition);
4735            setViewVisibility(mEnteringViews, View.VISIBLE);
4736        }
4737    }
4738
4739    private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
4740        private Rect mEpicenter;
4741
4742        public FixedEpicenterCallback(Rect epicenter) {
4743            mEpicenter = epicenter;
4744        }
4745
4746        @Override
4747        public Rect getEpicenter(Transition transition) {
4748            return mEpicenter;
4749        }
4750    };
4751}
4752