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