OSDN Git Service

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