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