summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/IWindowManager.aidl7
-rw-r--r--core/res/res/layout/expanded_menu_layout.xml2
-rw-r--r--core/res/res/layout/list_menu_item_layout.xml2
-rwxr-xr-xcore/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/themes.xml31
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java184
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java52
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java6
8 files changed, 260 insertions, 28 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 81cd798..c7bf8e3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -184,6 +184,13 @@ interface IWindowManager
*/
int watchRotation(IRotationWatcher watcher);
+ /**
+ * Determine the preferred edge of the screen to pin the compact options menu against.
+ * @return a Gravity value for the options menu panel
+ * @hide
+ */
+ int getPreferredOptionsPanelGravity();
+
/**
* Lock the device orientation to the current rotation. Sensor input will
* be ignored until thawRotation() is called.
diff --git a/core/res/res/layout/expanded_menu_layout.xml b/core/res/res/layout/expanded_menu_layout.xml
index 5d98773..f44a83f 100644
--- a/core/res/res/layout/expanded_menu_layout.xml
+++ b/core/res/res/layout/expanded_menu_layout.xml
@@ -16,5 +16,5 @@
<com.android.internal.view.menu.ExpandedMenuView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+android:id/expanded_menu"
- android:layout_width="296dip"
+ android:layout_width="?android:attr/panelMenuListWidth"
android:layout_height="wrap_content" />
diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml
index 57091a1..aaff4c7 100644
--- a/core/res/res/layout/list_menu_item_layout.xml
+++ b/core/res/res/layout/list_menu_item_layout.xml
@@ -16,7 +16,7 @@
<com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight">
+ android:layout_height="?android:attr/listPreferredItemHeightSmall">
<!-- Icon will be inserted here. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f70319b..eed41ea 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -439,6 +439,10 @@
<!-- Default appearance of panel text. -->
<attr name="panelTextAppearance" format="reference" />
+ <attr name="panelMenuIsCompact" format="boolean" />
+ <attr name="panelMenuListWidth" format="dimension" />
+ <attr name="panelMenuListTheme" format="reference" />
+
<!-- =================== -->
<!-- Other widget styles -->
<!-- =================== -->
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 615a37d..82299b8 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -185,6 +185,9 @@ please see themes_device_defaults.xml.
<item name="panelColorForeground">?android:attr/textColorPrimary</item>
<item name="panelTextAppearance">?android:attr/textAppearance</item>
+ <item name="panelMenuIsCompact">false</item>
+ <item name="panelMenuListWidth">296dip</item>
+
<!-- Scrollbar attributes -->
<item name="scrollbarFadeDuration">250</item>
<item name="scrollbarDefaultDelayBeforeFade">300</item>
@@ -755,6 +758,22 @@ please see themes_device_defaults.xml.
<item name="android:background">@null</item>
</style>
+ <style name="Theme.Holo.CompactMenu">
+ <!-- Menu/item attributes -->
+ <item name="android:itemTextAppearance">?android:attr/textAppearanceMedium</item>
+ <item name="android:listViewStyle">@android:style/Widget.Holo.ListView</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.DropDownUp</item>
+ <item name="android:background">@null</item>
+ </style>
+
+ <style name="Theme.Holo.Light.CompactMenu">
+ <!-- Menu/item attributes -->
+ <item name="android:itemTextAppearance">?android:attr/textAppearanceMedium</item>
+ <item name="android:listViewStyle">@android:style/Widget.Holo.Light.ListView</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.DropDownUp</item>
+ <item name="android:background">@null</item>
+ </style>
+
<!-- @hide -->
<style name="Theme.Dialog.AppError" parent="Theme.Holo.Dialog">
<item name="windowFrame">@null</item>
@@ -947,13 +966,17 @@ please see themes_device_defaults.xml.
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
<!-- Panel attributes -->
- <item name="panelBackground">@android:drawable/menu_background</item>
+ <item name="panelBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
<!-- These three attributes do not seems to be used by the framework. Declared public though -->
<item name="panelColorBackground">#000</item>
<item name="panelColorForeground">?android:attr/textColorPrimary</item>
<item name="panelTextAppearance">?android:attr/textAppearance</item>
+ <item name="panelMenuIsCompact">true</item>
+ <item name="panelMenuListWidth">250dip</item>
+ <item name="panelMenuListTheme">@android:style/Theme.Holo.CompactMenu</item>
+
<!-- Scrollbar attributes -->
<item name="scrollbarFadeDuration">250</item>
<item name="scrollbarDefaultDelayBeforeFade">300</item>
@@ -1244,13 +1267,17 @@ please see themes_device_defaults.xml.
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
<!-- Panel attributes -->
- <item name="panelBackground">@android:drawable/menu_background</item>
+ <item name="panelBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
<!-- These three attributes do not seems to be used by the framework. Declared public though -->
<item name="panelColorBackground">#000</item>
<item name="panelColorForeground">?android:attr/textColorPrimary</item>
<item name="panelTextAppearance">?android:attr/textAppearance</item>
+ <item name="panelMenuIsCompact">true</item>
+ <item name="panelMenuListWidth">250dip</item>
+ <item name="panelMenuListTheme">@android:style/Theme.Holo.Light.CompactMenu</item>
+
<!-- Scrollbar attributes -->
<item name="scrollbarFadeDuration">250</item>
<item name="scrollbarDefaultDelayBeforeFade">300</item>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 14f7c11..6dd4948 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -51,8 +51,11 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -61,6 +64,8 @@ import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Gravity;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
import android.view.InputQueue;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -85,6 +90,9 @@ import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
/**
* Android-specific Window.
* <p>
@@ -177,6 +185,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private int mUiOptions = 0;
+ static class WindowManagerHolder {
+ static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService("window"));
+ }
+
+ static final RotationWatcher sRotationWatcher = new RotationWatcher();
+
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
@@ -415,8 +430,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (st.iconMenuPresenter != null) {
st.iconMenuPresenter.saveHierarchyState(state);
}
- if (st.expandedMenuPresenter != null) {
- st.expandedMenuPresenter.saveHierarchyState(state);
+ if (st.listMenuPresenter != null) {
+ st.listMenuPresenter.saveHierarchyState(state);
}
// Remove the menu views since they need to be recreated
@@ -430,8 +445,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (st.iconMenuPresenter != null) {
st.iconMenuPresenter.restoreHierarchyState(state);
}
- if (st.expandedMenuPresenter != null) {
- st.expandedMenuPresenter.restoreHierarchyState(state);
+ if (st.listMenuPresenter != null) {
+ st.listMenuPresenter.restoreHierarchyState(state);
}
} else {
@@ -552,7 +567,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (!st.shownPanelView.hasFocus()) {
st.shownPanelView.requestFocus();
}
- } else if (!st.isInExpandedMode) {
+ } else if (!st.isInListMode()) {
width = MATCH_PARENT;
}
@@ -567,7 +582,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
st.decorView.mDefaultOpacity);
- lp.gravity = st.gravity;
+ if (st.isCompact) {
+ lp.gravity = getOptionsPanelGravity();
+ sRotationWatcher.addWindow(this);
+ } else {
+ lp.gravity = st.gravity;
+ }
+
lp.windowAnimations = st.windowAnimations;
wm.addView(st.decorView, lp);
@@ -610,6 +631,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (st.decorView != null) {
wm.removeView(st.decorView);
// Log.v(TAG, "Removing main menu from window manager.");
+ if (st.isCompact) {
+ sRotationWatcher.removeWindow(this);
+ }
}
if (doCallback) {
@@ -961,6 +985,36 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
/**
+ * Determine the gravity value for the options panel. This can
+ * differ in compact mode.
+ *
+ * @return gravity value to use for the panel window
+ */
+ private int getOptionsPanelGravity() {
+ try {
+ return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex);
+ return Gravity.CENTER | Gravity.BOTTOM;
+ }
+ }
+
+ void onOptionsPanelRotationChanged() {
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st == null) return;
+
+ final WindowManager.LayoutParams lp = st.decorView != null ?
+ (WindowManager.LayoutParams) st.decorView.getLayoutParams() : null;
+ if (lp != null) {
+ lp.gravity = getOptionsPanelGravity();
+ final ViewManager wm = getWindowManager();
+ if (wm != null) {
+ wm.updateViewLayout(st.decorView, lp);
+ }
+ }
+ }
+
+ /**
* Initializes the panel associated with the panel feature state. You must
* at the very least set PanelFeatureState.panel to the View implementing
* its contents. The default implementation gets the panel from the menu.
@@ -982,8 +1036,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
}
- MenuView menuView = st.isInExpandedMode
- ? st.getExpandedMenuView(mPanelMenuPresenterCallback)
+ MenuView menuView = st.isInListMode()
+ ? st.getListMenuView(mPanelMenuPresenterCallback)
: st.getIconMenuView(mPanelMenuPresenterCallback);
st.shownPanelView = (View) menuView;
@@ -3015,7 +3069,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
MenuBuilder menu;
IconMenuPresenter iconMenuPresenter;
- ListMenuPresenter expandedMenuPresenter;
+ ListMenuPresenter listMenuPresenter;
+
+ /** true if this menu will show in single-list compact mode */
+ boolean isCompact;
+
+ /** Theme resource ID for list elements of the panel menu */
+ int listPresenterTheme;
/**
* Whether the panel has been prepared (see
@@ -3065,11 +3125,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
refreshDecorView = false;
}
+ public boolean isInListMode() {
+ return isInExpandedMode || isCompact;
+ }
+
public boolean hasPanelItems() {
if (shownPanelView == null) return false;
- if (isInExpandedMode) {
- return expandedMenuPresenter.getAdapter().getCount() > 0;
+ if (isCompact || isInExpandedMode) {
+ return listMenuPresenter.getAdapter().getCount() > 0;
} else {
return ((ViewGroup) shownPanelView).getChildCount() > 0;
}
@@ -3081,10 +3145,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
public void clearMenuPresenters() {
if (menu != null) {
menu.removeMenuPresenter(iconMenuPresenter);
- menu.removeMenuPresenter(expandedMenuPresenter);
+ menu.removeMenuPresenter(listMenuPresenter);
}
iconMenuPresenter = null;
- expandedMenuPresenter = null;
+ listMenuPresenter = null;
}
void setStyle(Context context) {
@@ -3095,6 +3159,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
com.android.internal.R.styleable.Theme_panelFullBackground, 0);
windowAnimations = a.getResourceId(
com.android.internal.R.styleable.Theme_windowAnimationStyle, 0);
+ isCompact = a.getBoolean(
+ com.android.internal.R.styleable.Theme_panelMenuIsCompact, false);
+ listPresenterTheme = a.getResourceId(
+ com.android.internal.R.styleable.Theme_panelMenuListTheme,
+ com.android.internal.R.style.Theme_ExpandedMenu);
a.recycle();
}
@@ -3102,22 +3171,26 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
this.menu = menu;
}
- MenuView getExpandedMenuView(MenuPresenter.Callback cb) {
+ MenuView getListMenuView(MenuPresenter.Callback cb) {
if (menu == null) return null;
- getIconMenuView(cb); // Need this initialized to know where our offset goes
+ if (!isCompact) {
+ getIconMenuView(cb); // Need this initialized to know where our offset goes
+ }
- if (expandedMenuPresenter == null) {
- expandedMenuPresenter = new ListMenuPresenter(
- com.android.internal.R.layout.list_menu_item_layout,
- com.android.internal.R.style.Theme_ExpandedMenu);
- expandedMenuPresenter.setCallback(cb);
- expandedMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter);
- menu.addMenuPresenter(expandedMenuPresenter);
+ if (listMenuPresenter == null) {
+ listMenuPresenter = new ListMenuPresenter(
+ com.android.internal.R.layout.list_menu_item_layout, listPresenterTheme);
+ listMenuPresenter.setCallback(cb);
+ listMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter);
+ menu.addMenuPresenter(listMenuPresenter);
}
- expandedMenuPresenter.setItemIndexOffset(iconMenuPresenter.getNumActualItemsShown());
- MenuView result = expandedMenuPresenter.getMenuView(decorView);
+ if (iconMenuPresenter != null) {
+ listMenuPresenter.setItemIndexOffset(
+ iconMenuPresenter.getNumActualItemsShown());
+ }
+ MenuView result = listMenuPresenter.getMenuView(decorView);
return result;
}
@@ -3224,6 +3297,69 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
+ static class RotationWatcher extends IRotationWatcher.Stub {
+ private Handler mHandler;
+ private final Runnable mRotationChanged = new Runnable() {
+ public void run() {
+ dispatchRotationChanged();
+ }
+ };
+ private final ArrayList<WeakReference<PhoneWindow>> mWindows =
+ new ArrayList<WeakReference<PhoneWindow>>();
+ private boolean mIsWatching;
+
+ @Override
+ public void onRotationChanged(int rotation) throws RemoteException {
+ mHandler.post(mRotationChanged);
+ }
+
+ public void addWindow(PhoneWindow phoneWindow) {
+ synchronized (mWindows) {
+ if (!mIsWatching) {
+ try {
+ WindowManagerHolder.sWindowManager.watchRotation(this);
+ mHandler = new Handler();
+ mIsWatching = true;
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Couldn't start watching for device rotation", ex);
+ }
+ }
+ mWindows.add(new WeakReference<PhoneWindow>(phoneWindow));
+ }
+ }
+
+ public void removeWindow(PhoneWindow phoneWindow) {
+ synchronized (mWindows) {
+ int i = 0;
+ while (i < mWindows.size()) {
+ final WeakReference<PhoneWindow> ref = mWindows.get(i);
+ final PhoneWindow win = ref.get();
+ if (win == null || win == phoneWindow) {
+ mWindows.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ void dispatchRotationChanged() {
+ synchronized (mWindows) {
+ int i = 0;
+ while (i < mWindows.size()) {
+ final WeakReference<PhoneWindow> ref = mWindows.get(i);
+ final PhoneWindow win = ref.get();
+ if (win != null) {
+ win.onOptionsPanelRotationChanged();
+ i++;
+ } else {
+ mWindows.remove(i);
+ }
+ }
+ }
+ }
+ }
+
/**
* Simple implementation of MenuBuilder.Callback that:
* <li> Opens a submenu when selected.
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e0b5e17..c07531e 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -95,6 +95,7 @@ import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Display;
+import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -5163,6 +5164,57 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Apps that use the compact menu panel (as controlled by the panelMenuIsCompact
+ * theme attribute) on devices that feature a physical options menu key attempt to position
+ * their menu panel window along the edge of the screen nearest the physical menu key.
+ * This lowers the travel distance between invoking the menu panel and selecting
+ * a menu option.
+ *
+ * This method helps control where that menu is placed. Its current implementation makes
+ * assumptions about the menu key and its relationship to the screen based on whether
+ * the device's natural orientation is portrait (width < height) or landscape.
+ *
+ * The menu key is assumed to be located along the bottom edge of natural-portrait
+ * devices and along the right edge of natural-landscape devices. If these assumptions
+ * do not hold for the target device, this method should be changed to reflect that.
+ *
+ * @return A {@link Gravity} value for placing the options menu window
+ */
+ public int getPreferredOptionsPanelGravity() {
+ synchronized (mWindowMap) {
+ final int rotation = getRotation();
+
+ if (mInitialDisplayWidth < mInitialDisplayHeight) {
+ // On devices with a natural orientation of portrait
+ switch (rotation) {
+ default:
+ case Surface.ROTATION_0:
+ return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ case Surface.ROTATION_90:
+ return Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ case Surface.ROTATION_180:
+ return Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ case Surface.ROTATION_270:
+ return Gravity.LEFT | Gravity.CENTER_VERTICAL;
+ }
+ } else {
+ // On devices with a natural orientation of landscape
+ switch (rotation) {
+ default:
+ case Surface.ROTATION_0:
+ return Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ case Surface.ROTATION_90:
+ return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ case Surface.ROTATION_180:
+ return Gravity.LEFT | Gravity.CENTER_VERTICAL;
+ case Surface.ROTATION_270:
+ return Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ }
+ }
+ }
+ }
+
+ /**
* Starts the view server on the specified port.
*
* @param port The port to listener to.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index d94f369..5952c37 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Display_Delegate;
+import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -455,4 +456,9 @@ public class BridgeWindowManager implements IWindowManager {
return null;
}
+ @Override
+ public int getPreferredOptionsPanelGravity() throws RemoteException {
+ return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ }
+
}