summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml185
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java10
-rw-r--r--core/java/android/app/ActionBar.java116
-rw-r--r--core/java/android/app/Activity.java24
-rw-r--r--core/java/android/app/BackStackEntry.java96
-rw-r--r--core/java/android/app/Fragment.java105
-rw-r--r--core/java/android/app/FragmentManager.java71
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java116
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl6
-rw-r--r--core/java/android/view/ActionBarView.java93
-rw-r--r--core/java/android/view/Window.java2
-rw-r--r--core/java/android/webkit/WebView.java257
-rw-r--r--core/java/android/webkit/ZoomControlEmbedded.java3
-rw-r--r--core/java/android/webkit/ZoomManager.java183
-rw-r--r--core/java/android/widget/Spinner.java28
-rw-r--r--core/java/com/android/internal/app/SplitActionBar.java22
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl4
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java16
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/drawable/status_bar_item_background.xml2
-rw-r--r--core/res/res/values/arrays.xml24
-rwxr-xr-xcore/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/strings.xml5
-rw-r--r--core/res/res/values/themes.xml1
-rw-r--r--include/private/surfaceflinger/SharedBufferStack.h16
-rw-r--r--include/surfaceflinger/Surface.h6
-rw-r--r--libs/surfaceflinger/Layer.cpp48
-rw-r--r--libs/surfaceflinger/Layer.h11
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp22
-rw-r--r--libs/surfaceflinger_client/SharedBufferStack.cpp4
-rw-r--r--libs/surfaceflinger_client/Surface.cpp55
-rw-r--r--libs/surfaceflinger_client/SurfaceComposerClient.cpp4
-rw-r--r--libs/ui/GraphicBuffer.cpp3
-rw-r--r--libs/utils/ResourceTypes.cpp6
-rw-r--r--media/jni/android_media_MediaRecorder.cpp4
-rw-r--r--packages/SystemUI/AndroidManifest.xml14
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.pngbin1657 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.pngbin197 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.pngbin1531 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.pngbin11006 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.pngbin186 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.pngbin11006 -> 0 bytes
-rw-r--r--packages/SystemUI/res/layout/status_bar_latest_event.xml (renamed from core/res/res/layout/status_bar_latest_event.xml)8
-rw-r--r--packages/SystemUI/res/values/arrays.xml28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java (renamed from services/java/com/android/server/status/StorageNotification.java)9
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java (renamed from services/java/com/android/server/status/UsbStorageActivity.java)2
-rw-r--r--policy/com/android/internal/policy/impl/PhoneWindow.java21
-rwxr-xr-xpolicy/com/android/internal/policy/impl/PhoneWindowManager.java8
-rw-r--r--services/java/com/android/server/DemoDataSet.java140
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java78
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java2
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java30
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java (renamed from services/java/com/android/server/status/StatusBarManagerService.java)65
-rw-r--r--services/java/com/android/server/SystemServer.java55
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java7
-rw-r--r--services/java/com/android/server/WindowManagerService.java98
-rw-r--r--services/java/com/android/server/status/LatestItemView.java34
-rwxr-xr-xservices/java/com/android/server/status/package.html5
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java63
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java17
66 files changed, 1542 insertions, 918 deletions
diff --git a/api/current.xml b/api/current.xml
index ef8991f..287b3f3 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19417,7 +19417,7 @@
<parameter name="callback" type="android.app.ActionBar.Callback">
</parameter>
</method>
-<method name="setCustomNavigationView"
+<method name="setCustomNavigationMode"
return="void"
abstract="true"
native="false"
@@ -19471,7 +19471,7 @@
<parameter name="d" type="android.graphics.drawable.Drawable">
</parameter>
</method>
-<method name="setNavigationMode"
+<method name="setDropdownNavigationMode"
return="void"
abstract="true"
native="false"
@@ -19481,10 +19481,10 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="mode" type="int">
+<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
</method>
-<method name="setSubtitle"
+<method name="setStandardNavigationMode"
return="void"
abstract="true"
native="false"
@@ -19494,10 +19494,12 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
<parameter name="subtitle" type="java.lang.CharSequence">
</parameter>
</method>
-<method name="setTitle"
+<method name="setStandardNavigationMode"
return="void"
abstract="true"
native="false"
@@ -19565,7 +19567,7 @@
visibility="public"
>
</field>
-<field name="NAVIGATION_MODE_NORMAL"
+<field name="NAVIGATION_MODE_STANDARD"
type="int"
transient="false"
volatile="false"
@@ -19595,7 +19597,7 @@
deprecated="not deprecated"
visibility="public"
>
-<method name="onActionItemSelected"
+<method name="onActionItemClicked"
return="boolean"
abstract="true"
native="false"
@@ -19608,7 +19610,7 @@
<parameter name="item" type="android.view.MenuItem">
</parameter>
</method>
-<method name="onContextItemSelected"
+<method name="onContextItemClicked"
return="boolean"
abstract="true"
native="false"
@@ -19651,6 +19653,21 @@
<parameter name="menu" type="android.view.Menu">
</parameter>
</method>
+<method name="onNavigationItemSelected"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="itemPosition" type="int">
+</parameter>
+<parameter name="itemId" type="long">
+</parameter>
+</method>
<method name="onPrepareContextMode"
return="boolean"
abstract="true"
@@ -19698,7 +19715,7 @@
visibility="public"
>
</constructor>
-<method name="onActionItemSelected"
+<method name="onActionItemClicked"
return="boolean"
abstract="false"
native="false"
@@ -19711,7 +19728,7 @@
<parameter name="item" type="android.view.MenuItem">
</parameter>
</method>
-<method name="onContextItemSelected"
+<method name="onContextItemClicked"
return="boolean"
abstract="false"
native="false"
@@ -19754,6 +19771,21 @@
<parameter name="menu" type="android.view.Menu">
</parameter>
</method>
+<method name="onNavigationItemSelected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="itemPosition" type="int">
+</parameter>
+<parameter name="itemId" type="long">
+</parameter>
+</method>
<method name="onPrepareContextMode"
return="boolean"
abstract="false"
@@ -20274,6 +20306,17 @@
visibility="public"
>
</method>
+<method name="invalidateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isChangingConfigurations"
return="boolean"
abstract="false"
@@ -25511,7 +25554,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25522,7 +25565,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25533,7 +25576,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25544,7 +25587,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25577,7 +25620,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25588,7 +25631,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25599,7 +25642,7 @@
native="false"
synchronized="false"
static="false"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -25677,6 +25720,21 @@
<parameter name="nextAnim" type="int">
</parameter>
</method>
+<method name="onCreateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+<parameter name="inflater" type="android.view.MenuInflater">
+</parameter>
+</method>
<method name="onCreateView"
return="android.view.View"
abstract="false"
@@ -25757,6 +25815,32 @@
visibility="public"
>
</method>
+<method name="onOptionsItemSelected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.view.MenuItem">
+</parameter>
+</method>
+<method name="onOptionsMenuClosed"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
<method name="onPause"
return="void"
abstract="false"
@@ -25768,6 +25852,19 @@
visibility="public"
>
</method>
+<method name="onPrepareOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
<method name="onReady"
return="void"
abstract="false"
@@ -25827,6 +25924,19 @@
visibility="public"
>
</method>
+<method name="setHasOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="hasMenu" type="boolean">
+</parameter>
+</method>
<method name="setRetainInstance"
return="void"
abstract="false"
@@ -31839,6 +31949,19 @@
<parameter name="admin" type="android.content.ComponentName">
</parameter>
</method>
+<method name="getPasswordMinimumNonLetter"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
<method name="getPasswordMinimumNumeric"
return="int"
abstract="false"
@@ -32044,6 +32167,21 @@
<parameter name="length" type="int">
</parameter>
</method>
+<method name="setPasswordMinimumNonLetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
<method name="setPasswordMinimumNumeric"
return="void"
abstract="false"
@@ -189372,6 +189510,19 @@
visibility="protected"
>
</method>
+<method name="invalidatePanelMenu"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="featureId" type="int">
+</parameter>
+</method>
<method name="isActive"
return="boolean"
abstract="false"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 8263e75..39b3a20 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -175,6 +175,11 @@ public final class Bmgr {
private void doTransport() {
try {
String which = nextArg();
+ if (which == null) {
+ showUsage();
+ return;
+ }
+
String old = mBmgr.selectBackupTransport(which);
if (old == null) {
System.out.println("Unknown transport '" + which
@@ -318,6 +323,11 @@ public final class Bmgr {
private void doRestore() {
String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
if (arg.indexOf('.') >= 0) {
// it's a package name
doRestorePackage(arg);
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 77633c6..cbad72e 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -20,6 +20,8 @@ import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.widget.AdapterView;
+import android.widget.SpinnerAdapter;
/**
* This is the public interface to the contextual ActionBar.
@@ -29,12 +31,12 @@ import android.view.View;
*/
public abstract class ActionBar {
/**
- * Normal/standard navigation mode. Consists of either a logo or icon
+ * Standard navigation mode. Consists of either a logo or icon
* and title text with an optional subtitle. Clicking any of these elements
* will dispatch onActionItemSelected to the registered Callback with
* a MenuItem with item ID android.R.id.home.
*/
- public static final int NAVIGATION_MODE_NORMAL = 0;
+ public static final int NAVIGATION_MODE_STANDARD = 0;
/**
* Dropdown list navigation mode. Instead of static title text this mode
@@ -74,49 +76,52 @@ public abstract class ActionBar {
public abstract void setCallback(Callback callback);
/**
- * Set a custom navigation view.
+ * Set the action bar into custom navigation mode, supplying a view
+ * for custom navigation.
*
* Custom navigation views appear between the application icon and
* any action buttons and may use any space available there. Common
- * use cases for custom navigation views might include an address bar
- * for a browser or other navigation mechanisms that do not translate
- * well to provided navigation modes.
- *
- * Setting a non-null custom navigation view will also set the
- * navigation mode to NAVMODE_CUSTOM.
+ * use cases for custom navigation views might include an auto-suggesting
+ * address bar for a browser or other navigation mechanisms that do not
+ * translate well to provided navigation modes.
*
* @param view Custom navigation view to place in the ActionBar.
*/
- public abstract void setCustomNavigationView(View view);
+ public abstract void setCustomNavigationMode(View view);
/**
- * Set the ActionBar's title.
- *
- * This is set automatically to the name of your Activity,
- * but may be changed here.
+ * Set the action bar into dropdown navigation mode and supply an adapter
+ * that will provide views for navigation choices.
*
- * @param title Title text
+ * @param adapter An adapter that will provide views both to display
+ * the current navigation selection and populate views
+ * within the dropdown navigation menu.
*/
- public abstract void setTitle(CharSequence title);
-
+ public abstract void setDropdownNavigationMode(SpinnerAdapter adapter);
+
/**
- * Set the ActionBar's subtitle.
- *
- * The subtitle is usually displayed as a second line of text
- * under the title. Good for extended descriptions of activity state.
+ * Set the action bar into standard navigation mode, supplying a title and subtitle.
*
- * @param subtitle Subtitle text.
+ * Standard navigation mode is default. The title is automatically set to the
+ * name of your Activity. Subtitles are displayed underneath the title, usually
+ * in a smaller font or otherwise less prominently than the title. Subtitles are
+ * good for extended descriptions of activity state.
+ *
+ * @param title The action bar's title. null is treated as an empty string.
+ * @param subtitle The action bar's subtitle. null is treated as an empty string.
*/
- public abstract void setSubtitle(CharSequence subtitle);
-
+ public abstract void setStandardNavigationMode(CharSequence title, CharSequence subtitle);
+
/**
- * Set the navigation mode.
+ * Set the action bar into standard navigation mode, supplying a title and subtitle.
+ *
+ * Standard navigation mode is default. The title is automatically set to the
+ * name of your Activity.
*
- * @param mode One of {@link #NAVIGATION_MODE_NORMAL}, {@link #NAVIGATION_MODE_DROPDOWN_LIST},
- * {@link #NAVIGATION_MODE_TABS}, or {@link #NAVIGATION_MODE_CUSTOM}.
+ * @param title The action bar's title. null is treated as an empty string.
*/
- public abstract void setNavigationMode(int mode);
-
+ public abstract void setStandardNavigationMode(CharSequence title);
+
/**
* Set display options. This changes all display option bits at once. To change
* a limited subset of display options, see {@link #setDisplayOptions(int, int)}.
@@ -161,17 +166,38 @@ public abstract class ActionBar {
public abstract View getCustomNavigationView();
/**
- * @return The current ActionBar title.
+ * Returns the current ActionBar title in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar title or null.
*/
public abstract CharSequence getTitle();
/**
- * @return The current ActionBar subtitle.
+ * Returns the current ActionBar subtitle in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar subtitle or null.
*/
public abstract CharSequence getSubtitle();
/**
+ * Returns the current navigation mode. The result will be one of:
+ * <ul>
+ * <li>{@link #NAVIGATION_MODE_STANDARD}</li>
+ * <li>{@link #NAVIGATION_MODE_DROPDOWN_LIST}</li>
+ * <li>{@link #NAVIGATION_MODE_TABS}</li>
+ * <li>{@link #NAVIGATION_MODE_CUSTOM}</li>
+ * </ul>
+ *
* @return The current navigation mode.
+ *
+ * @see #setStandardNavigationMode(CharSequence)
+ * @see #setStandardNavigationMode(CharSequence, CharSequence)
+ * @see #setDropdownNavigationMode(SpinnerAdapter)
+ * @see #setCustomNavigationMode(View)
*/
public abstract int getNavigationMode();
@@ -201,7 +227,7 @@ public abstract class ActionBar {
* @return You must return true for actions to be displayed;
* if you return false they will not be shown.
*
- * @see #onActionItemSelected(MenuItem)
+ * @see #onActionItemClicked(MenuItem)
*/
public boolean onCreateActionMenu(Menu menu);
@@ -215,7 +241,7 @@ public abstract class ActionBar {
public boolean onUpdateActionMenu(Menu menu);
/**
- * This hook is called whenever an item in your action bar is selected.
+ * This hook is called whenever an action item in your action bar is clicked.
* The default implementation simply returns false to have the normal
* processing happen (sending a message to its handler). You can use this
* method for any items for which you would like to do processing without
@@ -225,14 +251,24 @@ public abstract class ActionBar {
* @return boolean Return false to allow normal menu processing to proceed,
* true to consume it here.
*/
- public boolean onActionItemSelected(MenuItem item);
-
+ public boolean onActionItemClicked(MenuItem item);
+
+ /**
+ * This method is called whenever a navigation item in your action bar
+ * is selected.
+ *
+ * @param itemPosition Position of the item clicked.
+ * @param itemId ID of the item clicked.
+ * @return True if the event was handled, false otherwise.
+ */
+ public boolean onNavigationItemSelected(int itemPosition, long itemId);
+
/*
* In progress
*/
public boolean onCreateContextMode(int modeId, Menu menu);
public boolean onPrepareContextMode(int modeId, Menu menu);
- public boolean onContextItemSelected(int modeId, MenuItem item);
+ public boolean onContextItemClicked(int modeId, MenuItem item);
}
/**
@@ -248,7 +284,7 @@ public abstract class ActionBar {
return false;
}
- public boolean onActionItemSelected(MenuItem item) {
+ public boolean onActionItemClicked(MenuItem item) {
return false;
}
@@ -260,7 +296,11 @@ public abstract class ActionBar {
return false;
}
- public boolean onContextItemSelected(int modeId, MenuItem item) {
+ public boolean onContextItemClicked(int modeId, MenuItem item) {
+ return false;
+ }
+
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
return false;
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d64f2c5..98505c5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -77,9 +77,6 @@ import android.widget.LinearLayout;
import com.android.internal.app.SplitActionBar;
import com.android.internal.policy.PolicyManager;
-import java.util.ArrayList;
-import java.util.HashMap;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -2242,7 +2239,9 @@ public class Activity extends ContextThemeWrapper
*/
public boolean onCreatePanelMenu(int featureId, Menu menu) {
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
- return onCreateOptionsMenu(menu);
+ boolean show = onCreateOptionsMenu(menu);
+ show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
+ return show;
}
return false;
}
@@ -2259,6 +2258,7 @@ public class Activity extends ContextThemeWrapper
public boolean onPreparePanel(int featureId, View view, Menu menu) {
if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
boolean goforit = onPrepareOptionsMenu(menu);
+ goforit |= mFragments.dispatchPrepareOptionsMenu(menu);
return goforit && menu.hasVisibleItems();
}
return true;
@@ -2289,7 +2289,10 @@ public class Activity extends ContextThemeWrapper
// doesn't call through to superclass's implmeentation of each
// of these methods below
EventLog.writeEvent(50000, 0, item.getTitleCondensed());
- return onOptionsItemSelected(item);
+ if (onOptionsItemSelected(item)) {
+ return true;
+ }
+ return mFragments.dispatchOptionsItemSelected(item);
case Window.FEATURE_CONTEXT_MENU:
EventLog.writeEvent(50000, 1, item.getTitleCondensed());
@@ -2312,6 +2315,7 @@ public class Activity extends ContextThemeWrapper
public void onPanelClosed(int featureId, Menu menu) {
switch (featureId) {
case Window.FEATURE_OPTIONS_PANEL:
+ mFragments.dispatchOptionsMenuClosed(menu);
onOptionsMenuClosed(menu);
break;
@@ -2322,6 +2326,15 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Declare that the options menu has changed, so should be recreated.
+ * The {@link #onCreateOptionsMenu(Menu)} method will be called the next
+ * time it needs to be displayed.
+ */
+ public void invalidateOptionsMenu() {
+ mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ /**
* Initialize the contents of the Activity's standard options menu. You
* should place your menu items in to <var>menu</var>.
*
@@ -3887,6 +3900,7 @@ public class Activity extends ContextThemeWrapper
fragment.mFromLayout = true;
fragment.mFragmentId = id;
fragment.mTag = tag;
+ fragment.mImmediateActivity = this;
mFragments.addFragment(fragment, true);
}
// If this fragment is newly instantiated (either right now, or
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 33e456d..5e9aea5 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -100,9 +100,10 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
static final int OP_NULL = 0;
static final int OP_ADD = 1;
- static final int OP_REMOVE = 2;
- static final int OP_HIDE = 3;
- static final int OP_SHOW = 4;
+ static final int OP_REPLACE = 2;
+ static final int OP_REMOVE = 3;
+ static final int OP_HIDE = 4;
+ static final int OP_SHOW = 5;
static final class Op {
Op next;
@@ -111,6 +112,7 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
Fragment fragment;
int enterAnim;
int exitAnim;
+ ArrayList<Fragment> removed;
}
Op mHead;
@@ -142,17 +144,25 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
public FragmentTransaction add(Fragment fragment, String tag) {
- return add(0, fragment, tag);
+ doAddOp(0, fragment, tag, OP_ADD);
+ return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment) {
- return add(containerViewId, fragment, null);
+ doAddOp(containerViewId, fragment, null, OP_ADD);
+ return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
- if (fragment.mActivity != null) {
+ doAddOp(containerViewId, fragment, tag, OP_ADD);
+ return this;
+ }
+
+ private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
+ if (fragment.mImmediateActivity != null) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
+ fragment.mImmediateActivity = mManager.mActivity;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
@@ -173,11 +183,9 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
Op op = new Op();
- op.cmd = OP_ADD;
+ op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
-
- return this;
}
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
@@ -188,21 +196,16 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
- if (mManager.mAdded != null) {
- for (int i=0; i<mManager.mAdded.size(); i++) {
- Fragment old = mManager.mAdded.get(i);
- if (old.mContainerId == containerViewId) {
- remove(old);
- }
- }
- }
- return add(containerViewId, fragment, tag);
+
+ doAddOp(containerViewId, fragment, tag, OP_REPLACE);
+ return this;
}
public FragmentTransaction remove(Fragment fragment) {
- if (fragment.mActivity == null) {
+ if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
+ fragment.mImmediateActivity = null;
Op op = new Op();
op.cmd = OP_REMOVE;
@@ -213,7 +216,7 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
public FragmentTransaction hide(Fragment fragment) {
- if (fragment.mActivity == null) {
+ if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
@@ -226,7 +229,7 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
public FragmentTransaction show(Fragment fragment) {
- if (fragment.mActivity == null) {
+ if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
@@ -278,6 +281,30 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
} break;
+ case OP_REPLACE: {
+ Fragment f = op.fragment;
+ if (mManager.mAdded != null) {
+ for (int i=0; i<mManager.mAdded.size(); i++) {
+ Fragment old = mManager.mAdded.get(i);
+ if (old.mContainerId == f.mContainerId) {
+ if (op.removed == null) {
+ op.removed = new ArrayList<Fragment>();
+ }
+ op.removed.add(old);
+ if (mAddToBackStack) {
+ old.mBackStackNesting++;
+ }
+ old.mNextAnim = op.exitAnim;
+ mManager.removeFragment(old, mTransition, mTransitionStyle);
+ }
+ }
+ }
+ if (mAddToBackStack) {
+ f.mBackStackNesting++;
+ }
+ f.mNextAnim = op.enterAnim;
+ mManager.addFragment(f, false);
+ } break;
case OP_REMOVE: {
Fragment f = op.fragment;
if (mAddToBackStack) {
@@ -312,6 +339,11 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
+ if (mManager.mNeedMenuInvalidate && mManager.mActivity != null) {
+ mManager.mActivity.invalidateOptionsMenu();
+ mManager.mNeedMenuInvalidate = false;
+ }
+
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
@@ -330,6 +362,24 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
FragmentManager.reverseTransit(mTransition),
mTransitionStyle);
} break;
+ case OP_REPLACE: {
+ Fragment f = op.fragment;
+ if (mAddToBackStack) {
+ f.mBackStackNesting--;
+ }
+ mManager.removeFragment(f,
+ FragmentManager.reverseTransit(mTransition),
+ mTransitionStyle);
+ if (op.removed != null) {
+ for (int i=0; i<op.removed.size(); i++) {
+ Fragment old = op.removed.get(i);
+ if (mAddToBackStack) {
+ old.mBackStackNesting--;
+ }
+ mManager.addFragment(old, false);
+ }
+ }
+ } break;
case OP_REMOVE: {
Fragment f = op.fragment;
if (mAddToBackStack) {
@@ -363,6 +413,10 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
mManager.moveToState(mManager.mCurState,
FragmentManager.reverseTransit(mTransition), mTransitionStyle, true);
+ if (mManager.mNeedMenuInvalidate && mManager.mActivity != null) {
+ mManager.mActivity.invalidateOptionsMenu();
+ mManager.mNeedMenuInvalidate = false;
+ }
}
public String getName() {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 58ae2d5..c734737 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -25,6 +25,9 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
@@ -157,6 +160,10 @@ public class Fragment implements ComponentCallbacks {
// Number of active back stack entries this fragment is in.
int mBackStackNesting;
+ // Set as soon as a fragment is added to a transaction (or removed),
+ // to be able to do validation.
+ Activity mImmediateActivity;
+
// Activity this fragment is attached to.
Activity mActivity;
@@ -184,6 +191,9 @@ public class Fragment implements ComponentCallbacks {
// If set this fragment is being retained across the current config change.
boolean mRetaining;
+ // If set this fragment has menu items to contribute.
+ boolean mHasMenu;
+
// Used to verify that subclasses call through to super class.
boolean mCalled;
@@ -270,28 +280,28 @@ public class Fragment implements ComponentCallbacks {
* the android:id value supplied in a layout or the container view ID
* supplied when adding the fragment.
*/
- public int getId() {
+ final public int getId() {
return mFragmentId;
}
/**
* Get the tag name of the fragment, if specified.
*/
- public String getTag() {
+ final public String getTag() {
return mTag;
}
/**
* Return the Activity this fragment is currently associated with.
*/
- public Activity getActivity() {
+ final public Activity getActivity() {
return mActivity;
}
/**
* Return true if the fragment is currently added to its activity.
*/
- public boolean isAdded() {
+ final public boolean isAdded() {
return mActivity != null && mActivity.mFragments.mAdded.contains(this);
}
@@ -300,7 +310,7 @@ public class Fragment implements ComponentCallbacks {
* it: (1) has been added, (2) has its view attached to the window, and
* (3) is not hidden.
*/
- public boolean isVisible() {
+ final public boolean isVisible() {
return isAdded() && !isHidden() && mView != null
&& mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
}
@@ -312,7 +322,7 @@ public class Fragment implements ComponentCallbacks {
* to other states -- that is, to be visible to the user, a fragment
* must be both started and not hidden.
*/
- public boolean isHidden() {
+ final public boolean isHidden() {
return mHidden;
}
@@ -344,11 +354,27 @@ public class Fragment implements ComponentCallbacks {
mRetainInstance = retain;
}
- public boolean getRetainInstance() {
+ final public boolean getRetainInstance() {
return mRetainInstance;
}
/**
+ * Report that this fragment would like to participate in populating
+ * the options menu by receiving a call to {@link #onCreateOptionsMenu(Menu)}
+ * and related methods.
+ *
+ * @param hasMenu If true, the fragment has menu items to contribute.
+ */
+ public void setHasOptionsMenu(boolean hasMenu) {
+ if (mHasMenu != hasMenu) {
+ mHasMenu = hasMenu;
+ if (isAdded() && !isHidden()) {
+ mActivity.invalidateOptionsMenu();
+ }
+ }
+ }
+
+ /**
* Call {@link Activity#startActivity(Intent)} on the fragment's
* containing Activity.
*/
@@ -526,4 +552,69 @@ public class Fragment implements ComponentCallbacks {
public void onDetach() {
mCalled = true;
}
+
+ /**
+ * Initialize the contents of the Activity's standard options menu. You
+ * should place your menu items in to <var>menu</var>. For this method
+ * to be called, you must have first called {@link #setHasMenu}. See
+ * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu}
+ * for more information.
+ *
+ * @param menu The options menu in which you place your items.
+ *
+ * @see #setHasMenu
+ * @see #onPrepareOptionsMenu
+ * @see #onOptionsItemSelected
+ */
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ }
+
+ /**
+ * Prepare the Screen's standard options menu to be displayed. This is
+ * called right before the menu is shown, every time it is shown. You can
+ * use this method to efficiently enable/disable items or otherwise
+ * dynamically modify the contents. See
+ * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu}
+ * for more information.
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ *
+ * @see #setHasMenu
+ * @see #onCreateOptionsMenu
+ */
+ public void onPrepareOptionsMenu(Menu menu) {
+ }
+
+ /**
+ * This hook is called whenever an item in your options menu is selected.
+ * The default implementation simply returns false to have the normal
+ * processing happen (calling the item's Runnable or sending a message to
+ * its Handler as appropriate). You can use this method for any items
+ * for which you would like to do processing without those other
+ * facilities.
+ *
+ * <p>Derived classes should call through to the base class for it to
+ * perform the default menu handling.
+ *
+ * @param item The menu item that was selected.
+ *
+ * @return boolean Return false to allow normal menu processing to
+ * proceed, true to consume it here.
+ *
+ * @see #onCreateOptionsMenu
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+ /**
+ * This hook is called whenever the options menu is being closed (either by the user canceling
+ * the menu with the back/menu button, or when an item is selected).
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ */
+ public void onOptionsMenuClosed(Menu menu) {
+ }
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index a10a191..b837c32 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -23,6 +23,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
@@ -82,6 +85,8 @@ public class FragmentManager {
int mCurState = Fragment.INITIALIZING;
Activity mActivity;
+ boolean mNeedMenuInvalidate;
+
// Temporary vars for state save and restore.
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
@@ -362,6 +367,9 @@ public class FragmentManager {
mAdded.add(fragment);
makeActive(fragment);
fragment.mAdded = true;
+ if (fragment.mHasMenu) {
+ mNeedMenuInvalidate = true;
+ }
if (moveToStateNow) {
moveToState(fragment, mCurState, 0, 0);
}
@@ -374,6 +382,9 @@ public class FragmentManager {
if (inactive) {
makeInactive(fragment);
}
+ if (fragment.mHasMenu) {
+ mNeedMenuInvalidate = true;
+ }
fragment.mAdded = false;
moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
transition, transitionStyle);
@@ -391,6 +402,9 @@ public class FragmentManager {
}
fragment.mView.setVisibility(View.GONE);
}
+ if (fragment.mAdded && fragment.mHasMenu) {
+ mNeedMenuInvalidate = true;
+ }
fragment.onHiddenChanged(true);
}
}
@@ -407,6 +421,9 @@ public class FragmentManager {
}
fragment.mView.setVisibility(View.VISIBLE);
}
+ if (fragment.mAdded && fragment.mHasMenu) {
+ mNeedMenuInvalidate = true;
+ }
fragment.onHiddenChanged(false);
}
}
@@ -673,6 +690,7 @@ public class FragmentManager {
"No instantiated fragment for index #" + fms.mAdded[i]);
}
f.mAdded = true;
+ f.mImmediateActivity = mActivity;
mAdded.add(f);
}
} else {
@@ -721,6 +739,59 @@ public class FragmentManager {
mActivity = null;
}
+ public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ boolean show = false;
+ if (mActive != null) {
+ for (int i=0; i<mAdded.size(); i++) {
+ Fragment f = mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu) {
+ show = true;
+ f.onCreateOptionsMenu(menu, inflater);
+ }
+ }
+ }
+ return show;
+ }
+
+ public boolean dispatchPrepareOptionsMenu(Menu menu) {
+ boolean show = false;
+ if (mActive != null) {
+ for (int i=0; i<mAdded.size(); i++) {
+ Fragment f = mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu) {
+ show = true;
+ f.onPrepareOptionsMenu(menu);
+ }
+ }
+ }
+ return show;
+ }
+
+ public boolean dispatchOptionsItemSelected(MenuItem item) {
+ if (mActive != null) {
+ for (int i=0; i<mAdded.size(); i++) {
+ Fragment f = mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu) {
+ if (f.onOptionsItemSelected(item)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public void dispatchOptionsMenuClosed(Menu menu) {
+ if (mActive != null) {
+ for (int i=0; i<mAdded.size(); i++) {
+ Fragment f = mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu) {
+ f.onOptionsMenuClosed(menu);
+ }
+ }
+ }
+ }
+
public static int reverseTransit(int transit) {
int rev = 0;
switch (transit) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 634adb0..3066f5c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -78,6 +78,15 @@ public class DevicePolicyManager {
= "android.app.action.ADD_DEVICE_ADMIN";
/**
+ * Activity action: send when any policy admin changes a policy.
+ * This is generally used to find out when a new policy is in effect.
+ *
+ * @hide
+ */
+ public static final String ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
+ = "android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED";
+
+ /**
* The ComponentName of the administrator component.
*
* @see #ACTION_ADD_DEVICE_ADMIN
@@ -206,9 +215,13 @@ public class DevicePolicyManager {
/**
* Constant for {@link #setPasswordQuality}: the user must have entered a
- * password containing numeric <em>and</em> alphabetic characters,
- * <em>and</em> special symbols. Note that quality constants are ordered so
- * that higher values are more restrictive.
+ * password containing at least a letter, a numerical digit and a special
+ * symbol, by default. With this password quality, passwords can be
+ * restricted to contain various sets of characters, like at least an
+ * uppercase letter, etc. These are specified using various methods,
+ * like {@link #setPasswordMinimumLowerCase(ComponentName, int)}. Note
+ * that quality constants are ordered so that higher values are more
+ * restrictive.
*/
public static final int PASSWORD_QUALITY_COMPLEX = 0x60000;
@@ -320,7 +333,8 @@ public class DevicePolicyManager {
* not take place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+ * default value is 0.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
@@ -344,7 +358,10 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of upper case letters required in the
- * password for all admins or a particular one.
+ * password for all admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
*
* @param admin The name of the admin component to check, or null to
* aggregate all admins.
@@ -371,7 +388,8 @@ public class DevicePolicyManager {
* not take place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+ * default value is 0.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
@@ -395,7 +413,10 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of lower case letters required in the
- * password for all admins or a particular one.
+ * password for all admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
*
* @param admin The name of the admin component to check, or null to
* aggregate all admins.
@@ -422,7 +443,8 @@ public class DevicePolicyManager {
* place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+ * default value is 1.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
@@ -445,7 +467,10 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of letters required in the password for all
- * admins or a particular one.
+ * admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumLetters(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
*
* @param admin The name of the admin component to check, or null to
* aggregate all admins.
@@ -471,7 +496,8 @@ public class DevicePolicyManager {
* not take place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+ * default value is 1.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
@@ -494,7 +520,10 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of numerical digits required in the password
- * for all admins or a particular one.
+ * for all admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
*
* @param admin The name of the admin component to check, or null to
* aggregate all admins.
@@ -520,7 +549,8 @@ public class DevicePolicyManager {
* place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+ * default value is 1.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
@@ -543,7 +573,10 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of symbols required in the password for all
- * admins or a particular one.
+ * admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumSymbols(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
*
* @param admin The name of the admin component to check, or null to
* aggregate all admins.
@@ -560,6 +593,59 @@ public class DevicePolicyManager {
return 0;
}
+ /**
+ * Called by an application that is administering the device to set the
+ * minimum number of non-letter characters (numerical digits or symbols)
+ * required in the password. After setting this, the user will not be able
+ * to enter a new password that is not at least as restrictive as what has
+ * been set. Note that the current password will remain until the user has
+ * set a new one, so the change does not take place immediately. To prompt
+ * the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} after
+ * setting this value. This constraint is only imposed if the administrator
+ * has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
+ * {@link #setPasswordQuality}. The default value is 0.
+ * <p>
+ * The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+ * this method; if it has not, a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated
+ * with.
+ * @param length The new desired minimum number of letters required in the
+ * password. A value of 0 means there is no restriction.
+ */
+ public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
+ if (mService != null) {
+ try {
+ mService.setPasswordMinimumNonLetter(admin, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current number of non-letter characters required in the
+ * password for all admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
+ *
+ * @param admin The name of the admin component to check, or null to
+ * aggregate all admins.
+ * @return The minimum number of letters required in the password.
+ */
+ public int getPasswordMinimumNonLetter(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getPasswordMinimumNonLetter(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
/**
* Called by an application that is administering the device to set the length
* of the password history. After setting this, the user will not be able to
@@ -885,11 +971,11 @@ public class DevicePolicyManager {
* @hide
*/
public void setActivePasswordState(int quality, int length, int letters, int uppercase,
- int lowercase, int numbers, int symbols) {
+ int lowercase, int numbers, int symbols, int nonletter) {
if (mService != null) {
try {
mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
- numbers, symbols);
+ numbers, symbols, nonletter);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fa31a37..3ada95c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -45,6 +45,9 @@ interface IDevicePolicyManager {
void setPasswordMinimumSymbols(in ComponentName who, int length);
int getPasswordMinimumSymbols(in ComponentName who);
+
+ void setPasswordMinimumNonLetter(in ComponentName who, int length);
+ int getPasswordMinimumNonLetter(in ComponentName who);
void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who);
@@ -71,7 +74,8 @@ interface IDevicePolicyManager {
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
- void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, int numbers, int symbols);
+ void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
+ int numbers, int symbols, int nonletter);
void reportFailedPasswordAttempt();
void reportSuccessfulPasswordAttempt();
}
diff --git a/core/java/android/view/ActionBarView.java b/core/java/android/view/ActionBarView.java
index 311274c..3ed345e 100644
--- a/core/java/android/view/ActionBarView.java
+++ b/core/java/android/view/ActionBarView.java
@@ -31,7 +31,10 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.SparseArray;
+import android.widget.AdapterView;
import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.android.internal.R;
@@ -78,12 +81,12 @@ public class ActionBarView extends ViewGroup {
private ImageView mLogoView;
private TextView mTitleView;
private TextView mSubtitleView;
- private View mNavigationView;
+ private Spinner mSpinner;
+ private View mCustomNavView;
private boolean mShowMenu;
private ActionMenuItem mLogoNavItem;
- private ActionMenu mNavMenu;
private ActionMenu mActionMenu;
private ActionMenu mOptionsMenu;
@@ -97,12 +100,24 @@ public class ActionBarView extends ViewGroup {
ActionView av = (ActionView) v;
ActionMenuItem item = (ActionMenuItem) av.menuItem;
- if (!mCallback.onActionItemSelected(item)) {
+ if (mCallback == null || !mCallback.onActionItemClicked(item)) {
item.invoke();
}
}
};
+ private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
+ new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView parent, View view, int position, long id) {
+ if (mCallback != null) {
+ mCallback.onNavigationItemSelected(position, id);
+ }
+ }
+ public void onNothingSelected(AdapterView parent) {
+ // Do nothing
+ }
+ };
+
private OnClickListener mHomeClickListener = null;
public ActionBarView(Context context, AttributeSet attrs) {
@@ -123,7 +138,7 @@ public class ActionBarView extends ViewGroup {
ApplicationInfo info = context.getApplicationInfo();
PackageManager pm = context.getPackageManager();
- mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_NORMAL);
+ mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
@@ -146,7 +161,7 @@ public class ActionBarView extends ViewGroup {
final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
if (customNavId != 0) {
LayoutInflater inflater = LayoutInflater.from(context);
- mNavigationView = (View) inflater.inflate(customNavId, null);
+ mCustomNavView = (View) inflater.inflate(customNavId, null);
mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
}
@@ -164,7 +179,7 @@ public class ActionBarView extends ViewGroup {
mHomeClickListener = new OnClickListener() {
public void onClick(View v) {
if (mCallback != null) {
- mCallback.onActionItemSelected(mLogoNavItem);
+ mCallback.onActionItemClicked(mLogoNavItem);
}
}
};
@@ -201,7 +216,7 @@ public class ActionBarView extends ViewGroup {
}
public void setCustomNavigationView(View view) {
- mNavigationView = view;
+ mCustomNavView = view;
if (view != null) {
setNavigationMode(ActionBar.NAVIGATION_MODE_CUSTOM);
}
@@ -258,25 +273,38 @@ public class ActionBarView extends ViewGroup {
final int oldMode = mNavigationMode;
if (mode != oldMode) {
switch (oldMode) {
- case ActionBar.NAVIGATION_MODE_NORMAL:
+ case ActionBar.NAVIGATION_MODE_STANDARD:
if (mTitleView != null) {
removeView(mTitleView);
mTitleView = null;
}
break;
+ case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
+ if (mSpinner != null) {
+ removeView(mSpinner);
+ mSpinner = null;
+ }
+ break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
- if (mNavigationView != null) {
- removeView(mNavigationView);
- mNavigationView = null;
+ if (mCustomNavView != null) {
+ removeView(mCustomNavView);
+ mCustomNavView = null;
}
+ break;
}
switch (mode) {
- case ActionBar.NAVIGATION_MODE_NORMAL:
+ case ActionBar.NAVIGATION_MODE_STANDARD:
initTitle();
break;
+ case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
+ mSpinner = new Spinner(mContext, null,
+ com.android.internal.R.attr.dropDownSpinnerStyle);
+ mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
+ addView(mSpinner);
+ break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
- addView(mNavigationView);
+ addView(mCustomNavView);
break;
}
mNavigationMode = mode;
@@ -284,8 +312,12 @@ public class ActionBarView extends ViewGroup {
}
}
+ public void setDropdownAdapter(SpinnerAdapter adapter) {
+ mSpinner.setAdapter(adapter);
+ }
+
public View getCustomNavigationView() {
- return mNavigationView;
+ return mCustomNavView;
}
public int getNavigationMode() {
@@ -451,7 +483,7 @@ public class ActionBarView extends ViewGroup {
}
switch (mNavigationMode) {
- case ActionBar.NAVIGATION_MODE_NORMAL:
+ case ActionBar.NAVIGATION_MODE_STANDARD:
if (mLogoView == null) {
initTitle();
}
@@ -459,15 +491,15 @@ public class ActionBarView extends ViewGroup {
case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
throw new UnsupportedOperationException(
- "Dropdown list navigation isn't supported yet!");
+ "Inflating dropdown list navigation isn't supported yet!");
case ActionBar.NAVIGATION_MODE_TABS:
throw new UnsupportedOperationException(
"Tab navigation isn't supported yet!");
case ActionBar.NAVIGATION_MODE_CUSTOM:
- if (mNavigationView != null) {
- addView(mNavigationView);
+ if (mCustomNavView != null) {
+ addView(mCustomNavView);
}
break;
}
@@ -520,14 +552,21 @@ public class ActionBarView extends ViewGroup {
}
switch (mNavigationMode) {
- case ActionBar.NAVIGATION_MODE_NORMAL:
+ case ActionBar.NAVIGATION_MODE_STANDARD:
if (mTitleView != null) {
measureChildView(mTitleView, availableWidth, childSpecHeight, mSpacing);
}
break;
+ case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
+ if (mSpinner != null) {
+ mSpinner.measure(
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+ break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
- if (mNavigationView != null) {
- mNavigationView.measure(
+ if (mCustomNavView != null) {
+ mCustomNavView.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@@ -561,15 +600,19 @@ public class ActionBarView extends ViewGroup {
}
switch (mNavigationMode) {
- case ActionBar.NAVIGATION_MODE_NORMAL:
+ case ActionBar.NAVIGATION_MODE_STANDARD:
if (mTitleView != null) {
x += positionChild(mTitleView, x, y, contentHeight) + mSpacing;
}
break;
-
+ case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
+ if (mSpinner != null) {
+ x += positionChild(mSpinner, x, y, contentHeight) + mSpacing;
+ }
+ break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
- if (mNavigationView != null) {
- x += positionChild(mNavigationView, x, y, contentHeight) + mSpacing;
+ if (mCustomNavView != null) {
+ x += positionChild(mCustomNavView, x, y, contentHeight) + mSpacing;
}
break;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index bbd9f04..5e6c538 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -817,6 +817,8 @@ public abstract class Window {
public abstract void togglePanel(int featureId, KeyEvent event);
+ public abstract void invalidatePanelMenu(int featureId);
+
public abstract boolean performPanelShortcut(int featureId,
int keyCode,
KeyEvent event,
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 19171f1..8a8df5a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -520,11 +520,6 @@ public class WebView extends AbsoluteLayout
private static final int MOTIONLESS_IGNORE = 3;
private int mHeldMotionless;
- // whether support multi-touch
- private boolean mSupportMultiTouch;
- // use the framework's ScaleGestureDetector to handle multi-touch
- private ScaleGestureDetector mScaleDetector;
-
// An instance for injecting accessibility in WebViews with disabled
// JavaScript or ones for which no accessibility script exists
private AccessibilityInjector mAccessibilityInjector;
@@ -873,16 +868,7 @@ public class WebView extends AbsoluteLayout
}
void updateMultiTouchSupport(Context context) {
- WebSettings settings = getSettings();
- mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
- && settings.supportZoom() && settings.getBuiltInZoomControls();
- if (mSupportMultiTouch && (mScaleDetector == null)) {
- mScaleDetector = new ScaleGestureDetector(context,
- new ScaleDetectorListener());
- } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
- mScaleDetector = null;
- }
+ mZoomManager.updateMultiTouchSupport(context);
}
private void init() {
@@ -2125,7 +2111,7 @@ public class WebView extends AbsoluteLayout
private Rect mLastGlobalRect;
Rect sendOurVisibleRect() {
- if (mZoomManager.mPreviewZoomOnly) return mLastVisibleRectSent;
+ if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
Rect rect = new Rect();
calcOurContentVisibleRect(rect);
@@ -2217,7 +2203,7 @@ public class WebView extends AbsoluteLayout
* @return true if new values were sent
*/
boolean sendViewSizeZoom(boolean force) {
- if (mZoomManager.mPreviewZoomOnly) return false;
+ if (mZoomManager.isPreventingWebkitUpdates()) return false;
int viewWidth = getViewWidth();
int newWidth = Math.round(viewWidth * mZoomManager.mInvActualScale);
@@ -2240,7 +2226,8 @@ public class WebView extends AbsoluteLayout
data.mHeight = newHeight;
data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.mTextWrapScale);
data.mScale = mZoomManager.mActualScale;
- data.mIgnoreHeight = mZoomManager.isZoomAnimating() && !mHeightCanMeasure;
+ data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
+ && !mHeightCanMeasure;
data.mAnchorX = mAnchorX;
data.mAnchorY = mAnchorY;
mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
@@ -3219,15 +3206,6 @@ public class WebView extends AbsoluteLayout
}
}
- /**
- * Need to adjust the WebTextView after a change in zoom, since mActualScale
- * has changed. This is especially important for password fields, which are
- * drawn by the WebTextView, since it conveys more information than what
- * webkit draws. Thus we need to reposition it to show in the correct
- * place.
- */
- private boolean mNeedToAdjustWebTextView;
-
private boolean didUpdateTextViewBounds(boolean allowIntersect) {
Rect contentBounds = nativeFocusCandidateNodeBounds();
Rect vBox = contentToViewRect(contentBounds);
@@ -3265,6 +3243,32 @@ public class WebView extends AbsoluteLayout
canvas.setDrawFilter(null);
}
+ private void onZoomAnimationStart() {
+ // If it is in password mode, turn it off so it does not draw misplaced.
+ if (inEditingMode() && nativeFocusCandidateIsPassword()) {
+ mWebTextView.setInPassword(false);
+ }
+ }
+
+ private void onZoomAnimationEnd() {
+ // adjust the edit text view if needed
+ if (inEditingMode() && didUpdateTextViewBounds(false) && nativeFocusCandidateIsPassword()) {
+ // If it is a password field, start drawing the WebTextView once
+ // again.
+ mWebTextView.setInPassword(true);
+ }
+ }
+
+ void onFixedLengthZoomAnimationStart() {
+ WebViewCore.pauseUpdatePicture(getWebViewCore());
+ onZoomAnimationStart();
+ }
+
+ void onFixedLengthZoomAnimationEnd() {
+ WebViewCore.resumeUpdatePicture(mWebViewCore);
+ onZoomAnimationEnd();
+ }
+
private void drawCoreAndCursorRing(Canvas canvas, int color,
boolean drawCursorRing) {
if (mDrawHistory) {
@@ -3273,7 +3277,7 @@ public class WebView extends AbsoluteLayout
return;
}
- boolean animateZoom = mZoomManager.isZoomAnimating();
+ boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
boolean animateScroll = ((!mScroller.isFinished()
|| mVelocityTracker != null)
&& (mTouchMode != TOUCH_DRAG_MODE ||
@@ -3292,39 +3296,7 @@ public class WebView extends AbsoluteLayout
}
}
if (animateZoom) {
- final float[] zoomValues = mZoomManager.animateZoom();
- final boolean isStillAnimating = mZoomManager.isZoomAnimating();
-
- if (isStillAnimating) {
- invalidate();
- } else {
- WebViewCore.resumeUpdatePicture(mWebViewCore);
- // call invalidate() again to draw with the final filters
- invalidate();
- if (mNeedToAdjustWebTextView) {
- mNeedToAdjustWebTextView = false;
- if (didUpdateTextViewBounds(false)
- && nativeFocusCandidateIsPassword()) {
- // If it is a password field, start drawing the
- // WebTextView once again.
- mWebTextView.setInPassword(true);
- }
- }
- }
-
- canvas.translate(zoomValues[0], zoomValues[1]);
- canvas.scale(zoomValues[2], zoomValues[2]);
-
- if (inEditingMode() && !mNeedToAdjustWebTextView && isStillAnimating) {
- // The WebTextView is up. Keep track of this so we can adjust
- // its size and placement when we finish zooming
- mNeedToAdjustWebTextView = true;
- // If it is in password mode, turn it off so it does not draw
- // misplaced.
- if (nativeFocusCandidateIsPassword()) {
- mWebTextView.setInPassword(false);
- }
- }
+ mZoomManager.animateZoom(canvas);
} else {
canvas.scale(mZoomManager.mActualScale, mZoomManager.mActualScale);
}
@@ -3339,7 +3311,7 @@ public class WebView extends AbsoluteLayout
invalidate();
}
mWebViewCore.drawContentPicture(canvas, color,
- (animateZoom || mZoomManager.mPreviewZoomOnly || UIAnimationsRunning),
+ (mZoomManager.isZoomAnimating() || UIAnimationsRunning),
animateScroll);
if (mNativeClass == 0) return;
// decide which adornments to draw
@@ -3350,9 +3322,8 @@ public class WebView extends AbsoluteLayout
if (!animateScroll) {
extras = DRAW_EXTRAS_FIND;
}
- } else if (mShiftIsPressed
- && !nativePageShouldHandleShiftAndArrows()) {
- if (!animateZoom && !mZoomManager.mPreviewZoomOnly) {
+ } else if (mShiftIsPressed && !nativePageShouldHandleShiftAndArrows()) {
+ if (!mZoomManager.isZoomAnimating()) {
extras = DRAW_EXTRAS_SELECTION;
nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
nativeSetSelectionPointer(!mTouchSelection, mZoomManager.mInvActualScale,
@@ -4425,79 +4396,6 @@ public class WebView extends AbsoluteLayout
private DragTracker mDragTracker;
private DragTrackerHandler mDragTrackerHandler;
- private class ScaleDetectorListener implements
- ScaleGestureDetector.OnScaleGestureListener {
-
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- // cancel the single touch handling
- cancelTouch();
- mZoomManager.dismissZoomPicker();
- // reset the zoom overview mode so that the page won't auto grow
- mZoomManager.mInZoomOverview = false;
- // If it is in password mode, turn it off so it does not draw
- // misplaced.
- if (inEditingMode() && nativeFocusCandidateIsPassword()) {
- mWebTextView.setInPassword(false);
- }
-
- mViewManager.startZoom();
-
- return true;
- }
-
- public void onScaleEnd(ScaleGestureDetector detector) {
- if (mZoomManager.mPreviewZoomOnly) {
- mZoomManager.mPreviewZoomOnly = false;
- mAnchorX = viewToContentX((int) mZoomManager.mZoomCenterX + mScrollX);
- mAnchorY = viewToContentY((int) mZoomManager.mZoomCenterY + mScrollY);
- // don't reflow when zoom in; when zoom out, do reflow if the
- // new scale is almost minimum scale;
- boolean reflowNow = !mZoomManager.canZoomOut()
- || (mZoomManager.mActualScale <= 0.8 * mZoomManager.mTextWrapScale);
- // force zoom after mPreviewZoomOnly is set to false so that the
- // new view size will be passed to the WebKit
- mZoomManager.refreshZoomScale(reflowNow);
- // call invalidate() to draw without zoom filter
- invalidate();
- }
- // adjust the edit text view if needed
- if (inEditingMode() && didUpdateTextViewBounds(false)
- && nativeFocusCandidateIsPassword()) {
- // If it is a password field, start drawing the
- // WebTextView once again.
- mWebTextView.setInPassword(true);
- }
- // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
- // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
- // may trigger the unwanted fling.
- mTouchMode = TOUCH_PINCH_DRAG;
- mConfirmMove = true;
- startTouch(detector.getFocusX(), detector.getFocusY(),
- mLastTouchTime);
-
- mViewManager.endZoom();
- }
-
- public boolean onScale(ScaleGestureDetector detector) {
- float scale = (float) (Math.round(detector.getScaleFactor()
- * mZoomManager.mActualScale * 100) / 100.0);
- if (mZoomManager.willScaleTriggerZoom(scale)) {
- mZoomManager.mPreviewZoomOnly = true;
- // limit the scale change per step
- if (scale > mZoomManager.mActualScale) {
- scale = Math.min(scale, mZoomManager.mActualScale * 1.25f);
- } else {
- scale = Math.max(scale, mZoomManager.mActualScale * 0.8f);
- }
- mZoomManager.setZoomCenter(detector.getFocusX(), detector.getFocusY());
- mZoomManager.setZoomScale(scale, false);
- invalidate();
- return true;
- }
- return false;
- }
- }
-
private boolean hitFocusedPlugin(int contentX, int contentY) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
@@ -4519,6 +4417,22 @@ public class WebView extends AbsoluteLayout
return mFullScreenHolder != null;
}
+ void onPinchToZoomAnimationStart() {
+ // cancel the single touch handling
+ cancelTouch();
+ onZoomAnimationStart();
+ }
+
+ void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
+ onZoomAnimationEnd();
+ // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
+ // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
+ // as it may trigger the unwanted fling.
+ mTouchMode = TOUCH_PINCH_DRAG;
+ mConfirmMove = true;
+ startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
@@ -4536,32 +4450,36 @@ public class WebView extends AbsoluteLayout
// FIXME: we may consider to give WebKit an option to handle multi-touch
// events later.
- if (mSupportMultiTouch && ev.getPointerCount() > 1) {
- if (mZoomManager.mMinZoomScale < mZoomManager.mMaxZoomScale) {
- mScaleDetector.onTouchEvent(ev);
- if (mScaleDetector.isInProgress()) {
- mLastTouchTime = eventTime;
+ if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1) {
+
+ // if the page disallows zoom, then skip multi-pointer action
+ if (mZoomManager.isZoomScaleFixed()) {
+ return true;
+ }
+
+ ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ detector.onTouchEvent(ev);
+
+ if (detector.isInProgress()) {
+ mLastTouchTime = eventTime;
+ return true;
+ }
+
+ x = detector.getFocusX();
+ y = detector.getFocusY();
+ action = ev.getAction() & MotionEvent.ACTION_MASK;
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ cancelTouch();
+ action = MotionEvent.ACTION_DOWN;
+ } else if (action == MotionEvent.ACTION_POINTER_UP) {
+ // set mLastTouchX/Y to the remaining point
+ mLastTouchX = x;
+ mLastTouchY = y;
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ // negative x or y indicate it is on the edge, skip it.
+ if (x < 0 || y < 0) {
return true;
}
- x = mScaleDetector.getFocusX();
- y = mScaleDetector.getFocusY();
- action = ev.getAction() & MotionEvent.ACTION_MASK;
- if (action == MotionEvent.ACTION_POINTER_DOWN) {
- cancelTouch();
- action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP) {
- // set mLastTouchX/Y to the remaining point
- mLastTouchX = x;
- mLastTouchY = y;
- } else if (action == MotionEvent.ACTION_MOVE) {
- // negative x or y indicate it is on the edge, skip it.
- if (x < 0 || y < 0) {
- return true;
- }
- }
- } else {
- // if the page disallow zoom, skip multi-pointer action
- return true;
}
} else {
action = ev.getAction();
@@ -4625,7 +4543,6 @@ public class WebView extends AbsoluteLayout
contentX, contentY) : false;
}
} else { // the normal case
- mZoomManager.mPreviewZoomOnly = false;
mTouchMode = TOUCH_INIT_MODE;
mDeferTouchProcess = (!inFullScreenMode()
&& mForwardTouchEvents) ? hitFocusedPlugin(
@@ -5686,11 +5603,7 @@ public class WebView extends AbsoluteLayout
int viewHeight = getViewHeightWithTitle();
float scale = Math.min((float) viewWidth / view.width,
(float) viewHeight / view.height);
- if (scale < mZoomManager.mMinZoomScale) {
- scale = mZoomManager.mMinZoomScale;
- } else if (scale > mZoomManager.mMaxZoomScale) {
- scale = mZoomManager.mMaxZoomScale;
- }
+ scale = mZoomManager.computeScaleWithLimits(scale);
if (!mZoomManager.willScaleTriggerZoom(scale)) {
if (contentToViewX(view.x) >= mScrollX
&& contentToViewX(view.x + view.width) <= mScrollX
@@ -5715,11 +5628,7 @@ public class WebView extends AbsoluteLayout
int viewHeight = getViewHeightWithTitle();
float scale = Math.min((float) viewWidth / docWidth, (float) viewHeight
/ docHeight);
- if (scale < mZoomManager.mMinZoomScale) {
- scale = mZoomManager.mMinZoomScale;
- } else if (scale > mZoomManager.mMaxZoomScale) {
- scale = mZoomManager.mMaxZoomScale;
- }
+ scale = mZoomManager.computeScaleWithLimits(scale);
if (!mZoomManager.willScaleTriggerZoom(scale)) {
pinScrollTo(contentToViewX(docX + docWidth / 2) - viewWidth / 2,
contentToViewY(docY + docHeight / 2) - viewHeight / 2,
@@ -5922,7 +5831,7 @@ public class WebView extends AbsoluteLayout
boolean immediate) {
// don't scroll while in zoom animation. When it is done, we will adjust
// the necessary components (e.g., WebTextView if it is in editing mode)
- if(mZoomManager.isZoomAnimating()) {
+ if (mZoomManager.isFixedLengthAnimationInProgress()) {
return false;
}
diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java
index 2036635..6e07ada 100644
--- a/core/java/android/webkit/ZoomControlEmbedded.java
+++ b/core/java/android/webkit/ZoomControlEmbedded.java
@@ -36,8 +36,7 @@ class ZoomControlEmbedded implements ZoomControlBase {
}
public void show() {
- if (!getControls().isVisible()
- && mZoomManager.mMinZoomScale < mZoomManager.mMaxZoomScale) {
+ if (!getControls().isVisible() && !mZoomManager.isZoomScaleFixed()) {
mZoomButtonsController.setVisible(true);
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 1540865..5ae5a6a4 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -16,11 +16,33 @@
package android.webkit;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Canvas;
import android.graphics.Point;
import android.os.SystemClock;
import android.util.Log;
+import android.view.ScaleGestureDetector;
import android.view.View;
+/**
+ * The ZoomManager is responsible for maintaining the WebView's current zoom
+ * level state. It is also responsible for managing the on-screen zoom controls
+ * as well as any animation of the WebView due to zooming.
+ *
+ * Currently, there are two methods for animating the zoom of a WebView.
+ *
+ * (1) The first method is triggered by startZoomAnimation(...) and is a fixed
+ * length animation where the final zoom scale is known at startup. This type of
+ * animation notifies webkit of the final scale BEFORE it animates. The animation
+ * is then done by scaling the CANVAS incrementally based on a stepping function.
+ *
+ * (2) The second method is triggered by a multi-touch pinch and the new scale
+ * is determined dynamically based on the user's gesture. This type of animation
+ * only notifies webkit of new scale AFTER the gesture is complete. The animation
+ * effect is achieved by scaling the VIEWS (both WebView and ViewManager.ChildView)
+ * to the new scale in response to events related to the user's gesture.
+ */
class ZoomManager {
static final String LOGTAG = "webviewZoom";
@@ -43,11 +65,11 @@ class ZoomManager {
static float DEFAULT_MIN_ZOOM_SCALE;
// actual scale limits, which can be set through a webpage viewport meta tag
- float mMaxZoomScale;
- float mMinZoomScale;
+ private float mMaxZoomScale;
+ private float mMinZoomScale;
// locks the minimum ZoomScale to the value currently set in mMinZoomScale
- boolean mMinZoomScaleFixed = true;
+ private boolean mMinZoomScaleFixed = true;
// while in the zoom overview mode, the page's width is fully fit to the
// current window. The page is alive, in another words, you can click to
@@ -73,9 +95,6 @@ class ZoomManager {
private static float MINIMUM_SCALE_INCREMENT = 0.01f;
- // set to true temporarily during ScaleGesture triggered zoom
- boolean mPreviewZoomOnly = false;
-
// the current computed zoom scale and its inverse.
float mActualScale;
float mInvActualScale;
@@ -104,6 +123,13 @@ class ZoomManager {
private long mZoomStart;
static final int ZOOM_ANIMATION_LENGTH = 500;
+ // whether support multi-touch
+ private boolean mSupportMultiTouch;
+
+ // use the framework's ScaleGestureDetector to handle multi-touch
+ private ScaleGestureDetector mScaleDetector;
+ private boolean mPinchToZoomAnimating = false;
+
public ZoomManager(WebView webView, CallbackProxy callbackProxy) {
mWebView = webView;
mCallbackProxy = callbackProxy;
@@ -150,6 +176,19 @@ class ZoomManager {
mInitialScale = scaleInPercent * 0.01f;
}
+ public float computeScaleWithLimits(float scale) {
+ if (scale < mMinZoomScale) {
+ scale = mMinZoomScale;
+ } else if (scale > mMaxZoomScale) {
+ scale = mMaxZoomScale;
+ }
+ return scale;
+ }
+
+ public boolean isZoomScaleFixed() {
+ return mMinZoomScale >= mMaxZoomScale;
+ }
+
public static final boolean exceedsMinScaleIncrement(float scaleA, float scaleB) {
return Math.abs(scaleA - scaleB) >= MINIMUM_SCALE_INCREMENT;
}
@@ -226,7 +265,7 @@ class ZoomManager {
mInvInitialZoomScale = 1.0f / oldScale;
mInvFinalZoomScale = 1.0f / mActualScale;
mZoomScale = mActualScale;
- WebViewCore.pauseUpdatePicture(mWebView.getWebViewCore());
+ mWebView.onFixedLengthZoomAnimationStart();
mWebView.invalidate();
return true;
} else {
@@ -235,25 +274,23 @@ class ZoomManager {
}
/**
- * Computes and returns the relevant data needed by the WebView's drawing
- * model to animate a zoom.
+ * This method is called by the WebView's drawing code when a fixed length zoom
+ * animation is occurring. Its purpose is to animate the zooming of the canvas
+ * to the desired scale which was specified in startZoomAnimation(...).
*
- * This method is to be called when a zoom animation is occurring. The
- * animation begins by calling startZoomAnimation(...). The caller can
- * check to see if the animation has completed by calling isZoomAnimating().
+ * A fixed length animation begins when startZoomAnimation(...) is called and
+ * continues until the ZOOM_ANIMATION_LENGTH time has elapsed. During that
+ * interval each time the WebView draws it calls this function which is
+ * responsible for generating the animation.
*
- * @return an array containing the values needed to animate the drawing
- * surface.
- * [0] = delta for the new scrollX position
- * [1] = delta for the new scrollY position
- * [2] = current zoom scale
+ * Additionally, the WebView can check to see if such an animation is currently
+ * in progress by calling isFixedLengthAnimationInProgress().
*/
- public float[] animateZoom() {
+ public void animateZoom(Canvas canvas) {
if (mZoomScale == 0) {
- Log.w(LOGTAG, "A WebView is attempting to animate a zoom when no " +
- "zoom is in progress");
- float[] result = {0, 0, mActualScale};
- return result;
+ Log.w(LOGTAG, "A WebView is attempting to perform a fixed length "
+ + "zoom animation when no zoom is in progress");
+ return;
}
float zoomScale;
@@ -262,10 +299,12 @@ class ZoomManager {
float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
zoomScale = 1.0f / (mInvInitialZoomScale
+ (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
+ mWebView.invalidate();
} else {
zoomScale = mZoomScale;
// set mZoomScale to be 0 as we have finished animating
mZoomScale = 0;
+ mWebView.onFixedLengthZoomAnimationEnd();
}
// calculate the intermediate scroll position. Since we need to use
// zoomScale, we can't use the WebView's pinLocX/Y functions directly.
@@ -281,11 +320,15 @@ class ZoomManager {
- titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight()
* zoomScale)) + titleHeight) + mWebView.getScrollY();
- float[] result = {tx, ty, zoomScale};
- return result;
+ canvas.translate(tx, ty);
+ canvas.scale(zoomScale, zoomScale);
}
public boolean isZoomAnimating() {
+ return isFixedLengthAnimationInProgress() || mPinchToZoomAnimating;
+ }
+
+ public boolean isFixedLengthAnimationInProgress() {
return mZoomScale != 0;
}
@@ -316,7 +359,7 @@ class ZoomManager {
float oldScale = mActualScale;
float oldInvScale = mInvActualScale;
- if (scale != mActualScale && !mPreviewZoomOnly) {
+ if (scale != mActualScale && !mPinchToZoomAnimating) {
mCallbackProxy.onScaleChanged(mActualScale, scale);
}
@@ -357,10 +400,98 @@ class ZoomManager {
}
}
+ public void updateMultiTouchSupport(Context context) {
+ // check the preconditions
+ assert mWebView.getSettings() != null;
+
+ WebSettings settings = mWebView.getSettings();
+ mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+ && settings.supportZoom() && settings.getBuiltInZoomControls();
+ if (mSupportMultiTouch && (mScaleDetector == null)) {
+ mScaleDetector = new ScaleGestureDetector(context, new ScaleDetectorListener());
+ } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
+ mScaleDetector = null;
+ }
+ }
+
+ public boolean supportsMultiTouchZoom() {
+ return mSupportMultiTouch;
+ }
+
+ /**
+ * Notifies the caller that the ZoomManager is requesting that scale related
+ * updates should not be sent to webkit. This can occur in cases where the
+ * ZoomManager is performing an animation and does not want webkit to update
+ * until the animation is complete.
+ *
+ * @return true if scale related updates should not be sent to webkit and
+ * false otherwise.
+ */
+ public boolean isPreventingWebkitUpdates() {
+ // currently only animating a multi-touch zoom prevents updates, but
+ // others can add their own conditions to this method if necessary.
+ return mPinchToZoomAnimating;
+ }
+
+ public ScaleGestureDetector getMultiTouchGestureDetector() {
+ return mScaleDetector;
+ }
+
+ private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener {
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ dismissZoomPicker();
+ // reset the zoom overview mode so that the page won't auto grow
+ mInZoomOverview = false;
+ mWebView.mViewManager.startZoom();
+ mWebView.onPinchToZoomAnimationStart();
+ return true;
+ }
+
+ public boolean onScale(ScaleGestureDetector detector) {
+ float scale = Math.round(detector.getScaleFactor() * mActualScale * 100) * 0.01f;
+ if (willScaleTriggerZoom(scale)) {
+ mPinchToZoomAnimating = true;
+ // limit the scale change per step
+ if (scale > mActualScale) {
+ scale = Math.min(scale, mActualScale * 1.25f);
+ } else {
+ scale = Math.max(scale, mActualScale * 0.8f);
+ }
+ setZoomCenter(detector.getFocusX(), detector.getFocusY());
+ setZoomScale(scale, false);
+ mWebView.invalidate();
+ return true;
+ }
+ return false;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ if (mPinchToZoomAnimating) {
+ mPinchToZoomAnimating = false;
+ mWebView.setViewSizeAnchor(mWebView.viewToContentX((int) mZoomCenterX
+ + mWebView.getScrollX()), mWebView.viewToContentY((int) mZoomCenterY
+ + mWebView.getScrollY()));
+ // don't reflow when zoom in; when zoom out, do reflow if the
+ // new scale is almost minimum scale;
+ boolean reflowNow = !canZoomOut() || (mActualScale <= 0.8 * mTextWrapScale);
+ // force zoom after mPreviewZoomOnly is set to false so that the
+ // new view size will be passed to the WebKit
+ refreshZoomScale(reflowNow);
+ // call invalidate() to draw without zoom filter
+ mWebView.invalidate();
+ }
+
+ mWebView.mViewManager.endZoom();
+ mWebView.onPinchToZoomAnimationEnd(detector);
+ }
+ }
+
public void onSizeChanged(int w, int h, int ow, int oh) {
// reset zoom and anchor to the top left corner of the screen
// unless we are already zooming
- if (!isZoomAnimating()) {
+ if (!isFixedLengthAnimationInProgress()) {
int visibleTitleHeight = mWebView.getVisibleTitleHeight();
mZoomCenterX = 0;
mZoomCenterY = visibleTitleHeight;
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index dde9a07..60e8568 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -251,8 +251,6 @@ public class Spinner extends AbsSpinner implements OnClickListener {
return child;
}
-
-
/**
* Helper for makeAndAddView to set the position of a view
* and fill out its layout paramters.
@@ -341,32 +339,6 @@ public class Spinner extends AbsSpinner implements OnClickListener {
return mPopup.getHintText();
}
- /*
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mPopup.onKeyDown(keyCode, event)) {
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (mPopup.onKeyUp(keyCode, event)) {
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (mPopup.onKeyPreIme(keyCode, event)) {
- return true;
- }
- return super.onKeyPreIme(keyCode, event);
- }
- */
-
/**
* <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
* into a ListAdapter.</p>
diff --git a/core/java/com/android/internal/app/SplitActionBar.java b/core/java/com/android/internal/app/SplitActionBar.java
index 9204c00..8129492 100644
--- a/core/java/com/android/internal/app/SplitActionBar.java
+++ b/core/java/com/android/internal/app/SplitActionBar.java
@@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable;
import android.view.ActionBarView;
import android.view.View;
import android.widget.LinearLayout;
+import android.widget.SpinnerAdapter;
/**
* SplitActionBar is the ActionBar implementation used
@@ -42,9 +43,24 @@ public class SplitActionBar extends ActionBar {
mActionView.setCallback(callback);
}
- public void setCustomNavigationView(View view) {
+ public void setCustomNavigationMode(View view) {
mActionView.setCustomNavigationView(view);
}
+
+ public void setDropdownNavigationMode(SpinnerAdapter adapter) {
+ mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
+ mActionView.setDropdownAdapter(adapter);
+ }
+
+ public void setStandardNavigationMode(CharSequence title) {
+ setStandardNavigationMode(title, null);
+ }
+
+ public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
+ mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
+ mActionView.setTitle(title);
+ mActionView.setSubtitle(subtitle);
+ }
public void setTitle(CharSequence title) {
mActionView.setTitle(title);
@@ -54,10 +70,6 @@ public class SplitActionBar extends ActionBar {
mActionView.setSubtitle(subtitle);
}
- public void setNavigationMode(int mode) {
- mActionView.setNavigationMode(mode);
- }
-
public void setDisplayOptions(int options) {
mActionView.setDisplayOptions(options);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 1f25b37..045c24f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -32,9 +32,11 @@ interface IStatusBarService
void removeIcon(String slot);
// ---- Methods below are for use by the status bar policy services ----
+ // You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications);
- void visibilityChanged(boolean visible);
+ void onPanelRevealed();
void onNotificationClick(String pkg, String tag, int id);
+ void onNotificationError(String pkg, String tag, int id, String message);
void onClearAllNotifications();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9983c02..0b62a67 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -164,6 +164,9 @@ public class LockPatternUtils {
return getDevicePolicyManager().getPasswordMinimumSymbols(null);
}
+ public int getRequestedPasswordMinimumNonLetter() {
+ return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
+ }
/**
* Returns the actual password mode, as set by keyguard after updating the password.
*
@@ -369,10 +372,10 @@ public class LockPatternUtils {
setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
- .size(), 0, 0, 0, 0, 0);
+ .size(), 0, 0, 0, 0, 0, 0);
} else {
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, 0);
}
} catch (FileNotFoundException fnfe) {
// Cant do much, unless we want to fail over to using the settings
@@ -441,6 +444,7 @@ public class LockPatternUtils {
int lowercase = 0;
int numbers = 0;
int symbols = 0;
+ int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
if (c >= 'A' && c <= 'Z') {
@@ -451,16 +455,18 @@ public class LockPatternUtils {
lowercase++;
} else if (c >= '0' && c <= '9') {
numbers++;
+ nonletter++;
} else {
symbols++;
+ nonletter++;
}
}
dpm.setActivePasswordState(Math.max(quality, computedQuality), password
- .length(), letters, uppercase, lowercase, numbers, symbols);
+ .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
} else {
// The password is not anything.
dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
}
// Add the password to the password history. We assume all
// password
@@ -483,7 +489,7 @@ public class LockPatternUtils {
setString(PASSWORD_HISTORY_KEY, passwordHistory);
} else {
dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
}
} catch (FileNotFoundException fnfe) {
// Cant do much, unless we want to fail over to using the settings provider
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4aff76f..a9dbaed 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -916,6 +916,13 @@
android:description="@string/permdesc_statusBar"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
+ @hide -->
+ <permission android:name="android.permission.STATUS_BAR_SERVICE"
+ android:label="@string/permlab_statusBarService"
+ android:description="@string/permdesc_statusBarService"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to force a BACK operation on whatever is the
top activity. -->
<permission android:name="android.permission.FORCE_BACK"
@@ -1277,9 +1284,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name="com.android.server.status.UsbStorageActivity"
- android:excludeFromRecents="true">
- </activity>
<activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
android:theme="@style/Theme.Dialog.Alert"
android:excludeFromRecents="true">
diff --git a/core/res/res/drawable/status_bar_item_background.xml b/core/res/res/drawable/status_bar_item_background.xml
index 088389b..425a502 100644
--- a/core/res/res/drawable/status_bar_item_background.xml
+++ b/core/res/res/drawable/status_bar_item_background.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
- android:drawable="@drawable/status_bar_item_background_pressed" />
+ android:drawable="@drawable/status_bar_item_background_pressed" />
<item android:state_focused="true" android:state_pressed="false"
android:drawable="@drawable/status_bar_item_background_focus" />
<item android:drawable="@drawable/status_bar_item_background_normal" />
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 0493756..aeee8af 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -117,28 +117,4 @@
<item>3</item>
</integer-array>
- <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
- icons in the status bar that are not notifications. -->
- <string-array name="status_bar_icon_order">
- <item><xliff:g id="id">ime</xliff:g></item>
- <item><xliff:g id="id">sync_failing</xliff:g></item>
- <item><xliff:g id="id">sync_active</xliff:g></item>
- <item><xliff:g id="id">gps</xliff:g></item>
- <item><xliff:g id="id">bluetooth</xliff:g></item>
- <item><xliff:g id="id">tty</xliff:g></item>
- <item><xliff:g id="id">speakerphone</xliff:g></item>
- <item><xliff:g id="id">mute</xliff:g></item>
- <item><xliff:g id="id">volume</xliff:g></item>
- <item><xliff:g id="id">tty</xliff:g></item>
- <item><xliff:g id="id">wifi</xliff:g></item>
- <item><xliff:g id="id">cdma_eri</xliff:g></item>
- <item><xliff:g id="id">data_connection</xliff:g></item>
- <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
- <item><xliff:g id="id">phone_signal</xliff:g></item>
- <item><xliff:g id="id">battery</xliff:g></item>
- <item><xliff:g id="id">alarm_clock</xliff:g></item>
- <item><xliff:g id="id">secure</xliff:g></item>
- <item><xliff:g id="id">clock</xliff:g></item>
- </string-array>
-
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index af8d91c..d1fc0a3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -397,6 +397,8 @@
<attr name="horizontalScrollViewStyle" format="reference" />
<!-- Default Spinner style. -->
<attr name="spinnerStyle" format="reference" />
+ <!-- Dropdown Spinner style. -->
+ <attr name="dropDownSpinnerStyle" format="reference" />
<!-- Default Star style. -->
<attr name="starStyle" format="reference" />
<!-- Default TabWidget style. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1946388..cffcd1d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -19,7 +19,35 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Component to be used as the status bar service. Must implement the IStatusBar
+ interface. This name is in the ComponentName flattened format (package/class) -->
+ <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string>
+
+ <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
+ icons in the status bar that are not notifications. -->
+ <string-array name="config_statusBarIcons">
+ <item><xliff:g id="id">ime</xliff:g></item>
+ <item><xliff:g id="id">sync_failing</xliff:g></item>
+ <item><xliff:g id="id">sync_active</xliff:g></item>
+ <item><xliff:g id="id">gps</xliff:g></item>
+ <item><xliff:g id="id">bluetooth</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">speakerphone</xliff:g></item>
+ <item><xliff:g id="id">mute</xliff:g></item>
+ <item><xliff:g id="id">volume</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">wifi</xliff:g></item>
+ <item><xliff:g id="id">cdma_eri</xliff:g></item>
+ <item><xliff:g id="id">data_connection</xliff:g></item>
+ <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
+ <item><xliff:g id="id">phone_signal</xliff:g></item>
+ <item><xliff:g id="id">battery</xliff:g></item>
+ <item><xliff:g id="id">alarm_clock</xliff:g></item>
+ <item><xliff:g id="id">secure</xliff:g></item>
+ <item><xliff:g id="id">clock</xliff:g></item>
+ </string-array>
+
<!-- Flag indicating whether the surface flinger has limited
alpha compositing functionality in hardware. If set, the window
manager will disable alpha trasformation in animations where not
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a30b44b..5d2ca78 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -393,6 +393,11 @@
the status bar or add and remove system icons.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_statusBarService">status bar</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_statusBarService">Allows the application to be the status bar.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_expandStatusBar">expand/collapse status bar</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_expandStatusBar">Allows application to
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 32c5f47..bc5f610 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -169,6 +169,7 @@
<item name="scrollViewStyle">@android:style/Widget.ScrollView</item>
<item name="horizontalScrollViewStyle">@android:style/Widget.HorizontalScrollView</item>
<item name="spinnerStyle">@android:style/Widget.Spinner</item>
+ <item name="dropDownSpinnerStyle">@android:style/Widget.Spinner.DropDown</item>
<item name="starStyle">@android:style/Widget.CompoundButton.Star</item>
<item name="tabWidgetStyle">@android:style/Widget.TabWidget</item>
<item name="textViewStyle">@android:style/Widget.TextView</item>
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index c11c855..633b543 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -43,15 +43,6 @@ namespace android {
* unless they are in use by the server, which is only the case for the last
* dequeue-able buffer. When these various conditions are not met, the caller
* waits until the condition is met.
- *
- *
- * CAVEATS:
- *
- * In the current implementation there are several limitations:
- * - buffers must be locked in the same order they've been dequeued
- * - buffers must be enqueued in the same order they've been locked
- * - dequeue() is not reentrant
- * - no error checks are done on the condition above
*
*/
@@ -269,7 +260,9 @@ private:
// ----------------------------------------------------------------------------
-class SharedBufferServer : public SharedBufferBase
+class SharedBufferServer
+ : public SharedBufferBase,
+ public LightRefBase<SharedBufferServer>
{
public:
SharedBufferServer(SharedClient* sharedClient, int surface, int num,
@@ -290,6 +283,9 @@ public:
private:
+ friend class LightRefBase<SharedBufferServer>;
+ ~SharedBufferServer();
+
/*
* BufferList is basically a fixed-capacity sorted-vector of
* unsigned 5-bits ints using a 32-bits int as storage.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index ac01ce5..f333911 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -60,7 +60,6 @@ public:
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
- SurfaceID ID() const { return mToken; }
uint32_t getFlags() const { return mFlags; }
uint32_t getIdentity() const { return mIdentity; }
@@ -145,6 +144,9 @@ public:
uint32_t reserved[2];
};
+ static status_t writeToParcel(
+ const sp<Surface>& control, Parcel* parcel);
+
static sp<Surface> readFromParcel(
const Parcel& data, const sp<Surface>& other);
@@ -153,7 +155,6 @@ public:
}
bool isValid();
- SurfaceID ID() const { return mToken; }
uint32_t getFlags() const { return mFlags; }
uint32_t getIdentity() const { return mIdentity; }
@@ -267,7 +268,6 @@ private:
SharedBufferClient* mSharedBufferClient;
status_t mInitCheck;
sp<ISurface> mSurface;
- SurfaceID mToken;
uint32_t mIdentity;
PixelFormat mFormat;
uint32_t mFlags;
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
index a94fdd4..e7247bd 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -76,15 +76,18 @@ Layer::~Layer()
status_t Layer::setToken(const sp<UserClient>& userClient,
SharedClient* sharedClient, int32_t token)
{
- SharedBufferServer* lcblk = new SharedBufferServer(
+ sp<SharedBufferServer> lcblk = new SharedBufferServer(
sharedClient, token, mBufferManager.getDefaultBufferCount(),
getIdentity());
status_t err = mUserClientRef.setToken(userClient, lcblk, token);
- if (err != NO_ERROR) {
- LOGE("ClientRef::setToken(%p, %p, %u) failed",
- userClient.get(), lcblk, token);
- delete lcblk;
+
+ LOGE_IF(err != NO_ERROR,
+ "ClientRef::setToken(%p, %p, %u) failed",
+ userClient.get(), lcblk.get(), token);
+
+ if (err == NO_ERROR) {
+ // we need to free the buffers associated with this surface
}
return err;
@@ -95,6 +98,11 @@ int32_t Layer::getToken() const
return mUserClientRef.getToken();
}
+sp<UserClient> Layer::getClient() const
+{
+ return mUserClientRef.getClient();
+}
+
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
// in the purgatory list
void Layer::onRemoved()
@@ -626,11 +634,10 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
// ---------------------------------------------------------------------------
Layer::ClientRef::ClientRef()
- : mToken(-1) {
+ : mControlBlock(0), mToken(-1) {
}
Layer::ClientRef::~ClientRef() {
- delete lcblk;
}
int32_t Layer::ClientRef::getToken() const {
@@ -638,14 +645,25 @@ int32_t Layer::ClientRef::getToken() const {
return mToken;
}
+sp<UserClient> Layer::ClientRef::getClient() const {
+ Mutex::Autolock _l(mLock);
+ return mUserClient.promote();
+}
+
status_t Layer::ClientRef::setToken(const sp<UserClient>& uc,
- SharedBufferServer* sharedClient, int32_t token) {
+ const sp<SharedBufferServer>& sharedClient, int32_t token) {
Mutex::Autolock _l(mLock);
- if (mToken >= 0)
- return INVALID_OPERATION;
+
+ { // scope for strong mUserClient reference
+ sp<UserClient> userClient(mUserClient.promote());
+ if (mUserClient != 0 && mControlBlock != 0) {
+ mControlBlock->setStatus(NO_INIT);
+ }
+ }
+
mUserClient = uc;
mToken = token;
- lcblk = sharedClient;
+ mControlBlock = sharedClient;
return NO_ERROR;
}
@@ -657,12 +675,16 @@ sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const {
// it makes sure the UserClient (and its associated shared memory)
// won't go away while we're accessing it.
Layer::ClientRef::Access::Access(const ClientRef& ref)
- : lcblk(0)
+ : mControlBlock(0)
{
Mutex::Autolock _l(ref.mLock);
mUserClientStrongRef = ref.mUserClient.promote();
if (mUserClientStrongRef != 0)
- lcblk = ref.lcblk;
+ mControlBlock = ref.mControlBlock;
+}
+
+Layer::ClientRef::Access::~Access()
+{
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index d396ecf..dcb27a0 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -60,6 +60,7 @@ public:
// associate a UserClient to this Layer
status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx);
int32_t getToken() const;
+ sp<UserClient> getClient() const;
// Set this Layer's buffers size
void setBufferSize(uint32_t w, uint32_t h);
@@ -119,24 +120,26 @@ private:
ClientRef& operator = (const ClientRef& rhs);
mutable Mutex mLock;
// binder thread, page-flip thread
- SharedBufferServer* lcblk;
+ sp<SharedBufferServer> mControlBlock;
wp<UserClient> mUserClient;
int32_t mToken;
public:
ClientRef();
~ClientRef();
int32_t getToken() const;
+ sp<UserClient> getClient() const;
status_t setToken(const sp<UserClient>& uc,
- SharedBufferServer* sharedClient, int32_t token);
+ const sp<SharedBufferServer>& sharedClient, int32_t token);
sp<UserClient> getUserClientUnsafe() const;
class Access {
Access(const Access& rhs);
Access& operator = (const Access& rhs);
sp<UserClient> mUserClientStrongRef;
- SharedBufferServer* lcblk;
+ sp<SharedBufferServer> mControlBlock;
public:
Access(const ClientRef& ref);
- inline SharedBufferServer* get() const { return lcblk; }
+ ~Access();
+ inline SharedBufferServer* get() const { return mControlBlock.get(); }
};
friend class Access;
};
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0f73774..4dea62f 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -1718,7 +1718,10 @@ void UserClient::detachLayer(const Layer* layer)
{
int32_t name = layer->getToken();
if (name >= 0) {
- android_atomic_and(~(1LU<<name), &mBitmap);
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) {
+ LOGW("token %d wasn't marked as used %08x", name, int(mBitmap));
+ }
}
}
@@ -1732,24 +1735,31 @@ ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
sp<Layer> layer(mFlinger->getLayer(sur));
if (layer == 0) return name;
- // this layer already has a token, just return it
- // FIXME: we should check that this token is for the same client
+ // if this layer already has a token, just return it
name = layer->getToken();
- if (name >= 0) return name;
+ if ((name >= 0) && (layer->getClient() == this))
+ return name;
name = 0;
do {
int32_t mask = 1LU<<name;
if ((android_atomic_or(mask, &mBitmap) & mask) == 0) {
// we found and locked that name
- layer->setToken(const_cast<UserClient*>(this), ctrlblk, name);
+ status_t err = layer->setToken(
+ const_cast<UserClient*>(this), ctrlblk, name);
+ if (err != NO_ERROR) {
+ // free the name
+ android_atomic_and(~mask, &mBitmap);
+ name = err;
+ }
break;
}
if (++name > 31)
name = NO_MEMORY;
} while(name >= 0);
- //LOGD("getTokenForSurface(%p) => %d", sur->asBinder().get(), name);
+ //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)",
+ // sur->asBinder().get(), name, this, mBitmap);
return name;
}
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 1dd8642..d67a589 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -494,6 +494,10 @@ SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
}
}
+SharedBufferServer::~SharedBufferServer()
+{
+}
+
ssize_t SharedBufferServer::retireAndLock()
{
RWLock::AutoRLock _l(mLock);
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 6fe4c4a..8617d94a 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -236,17 +236,15 @@ status_t SurfaceControl::validate() const
status_t SurfaceControl::writeSurfaceToParcel(
const sp<SurfaceControl>& control, Parcel* parcel)
{
- uint32_t flags = 0;
- uint32_t format = 0;
+ sp<ISurface> sur;
uint32_t identity = 0;
uint32_t width = 0;
uint32_t height = 0;
- sp<SurfaceComposerClient> client;
- sp<ISurface> sur;
+ uint32_t format = 0;
+ uint32_t flags = 0;
if (SurfaceControl::isValid(control)) {
- identity = control->mIdentity;
- client = control->mClient;
sur = control->mSurface;
+ identity = control->mIdentity;
width = control->mWidth;
height = control->mHeight;
format = control->mFormat;
@@ -349,6 +347,33 @@ Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
init();
}
+status_t Surface::writeToParcel(
+ const sp<Surface>& surface, Parcel* parcel)
+{
+ sp<ISurface> sur;
+ uint32_t identity = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ uint32_t format = 0;
+ uint32_t flags = 0;
+ if (Surface::isValid(surface)) {
+ sur = surface->mSurface;
+ identity = surface->mIdentity;
+ width = surface->mWidth;
+ height = surface->mHeight;
+ format = surface->mFormat;
+ flags = surface->mFlags;
+ }
+ parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+ parcel->writeInt32(identity);
+ parcel->writeInt32(width);
+ parcel->writeInt32(height);
+ parcel->writeInt32(format);
+ parcel->writeInt32(flags);
+ return NO_ERROR;
+
+}
+
sp<Surface> Surface::readFromParcel(
const Parcel& data, const sp<Surface>& other)
{
@@ -385,11 +410,11 @@ void Surface::init()
mBuffers.insertAt(0, 2);
if (mSurface != 0 && mClient.initCheck() == NO_ERROR) {
- mToken = mClient.getTokenForSurface(mSurface);
- if (mToken >= 0) {
+ int32_t token = mClient.getTokenForSurface(mSurface);
+ if (token >= 0) {
mSharedBufferClient = new SharedBufferClient(
- mClient.getSharedClient(), mToken, 2, mIdentity);
- mInitCheck = mClient.getSharedClient()->validate(mToken);
+ mClient.getSharedClient(), token, 2, mIdentity);
+ mInitCheck = mClient.getSharedClient()->validate(token);
}
}
}
@@ -421,7 +446,7 @@ status_t Surface::validate() const
{
// check that we initialized ourself properly
if (mInitCheck != NO_ERROR) {
- LOGE("invalid token (%d, identity=%u)", mToken, mIdentity);
+ LOGE("invalid token (identity=%u)", mIdentity);
return mInitCheck;
}
@@ -437,17 +462,17 @@ status_t Surface::validate() const
}
if (mIdentity != identity) {
- LOGE("[Surface] using an invalid surface id=%d, "
+ LOGE("[Surface] using an invalid surface, "
"identity=%u should be %d",
- mToken, mIdentity, identity);
+ mIdentity, identity);
return NO_INIT;
}
// check the surface didn't become invalid
status_t err = mSharedBufferClient->getStatus();
if (err != NO_ERROR) {
- LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
- mToken, mIdentity, err, strerror(-err));
+ LOGE("surface (identity=%u) is invalid, err=%d (%s)",
+ mIdentity, err, strerror(-err));
return err;
}
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 5ac0d5d..4096ac6 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -305,9 +305,7 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(
sp<ISurface> surface = mClient->createSurface(&data, pid, name,
display, w, h, format, flags);
if (surface != 0) {
- if (uint32_t(data.token) < SharedBufferStack::NUM_LAYERS_MAX) {
- result = new SurfaceControl(this, surface, data, w, h, format, flags);
- }
+ result = new SurfaceControl(this, surface, data, w, h, format, flags);
}
}
return result;
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 3ddde38..4b5f025 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -111,6 +111,9 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
if (mOwner != ownData)
return INVALID_OPERATION;
+ if (handle && w==width && h==height && f==format && reqUsage==usage)
+ return NO_ERROR;
+
if (handle) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 954255b..4362d14 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -317,6 +317,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
mStringPoolSize =
(mHeader->header.size-mHeader->stringsStart)/charSize;
} else {
+ // check invariant: styles starts before end of data
+ if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) {
+ LOGW("Bad style block: style block starts at %d past data size of %d\n",
+ (int)mHeader->stylesStart, (int)mHeader->header.size);
+ return (mError=BAD_TYPE);
+ }
// check invariant: styles follow the strings
if (mHeader->stylesStart <= mHeader->stringsStart) {
LOGW("Bad style block: style block starts at %d, before strings at %d\n",
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 8cf2e51..c113ffe 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -317,8 +317,8 @@ android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
- const sp<Surface>& native_surface = get_surface(env, surface);
- LOGI("prepare: surface=%p (id=%d)", native_surface.get(), native_surface->ID());
+ const sp<Surface> native_surface = get_surface(env, surface);
+ LOGI("prepare: surface=%p (identity=%d)", native_surface.get(), native_surface->getIdentity());
if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) {
return;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3f5b69d..74d87ba 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -2,21 +2,21 @@
package="com.android.systemui"
android:sharedUserId="android.uid.system">
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+
<application
android:allowClearUserData="false"
android:label="@string/app_label"
android:icon="@drawable/ic_launcher_settings">
- <receiver
- android:name=".statusbar.StatusBarStarter"
- >
- <intent-filter>
- <action android:name="com.android.internal.policy.statusbar.START" />
- </intent-filter>
- </receiver>
<service
android:name=".statusbar.PhoneStatusBarService"
android:exported="false"
/>
+
+ <activity android:name=".usb.UsbStorageActivity"
+ android:excludeFromRecents="true">
+ </activity>
+
</application>
</manifest>
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png
deleted file mode 100644
index 0876bc6..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png
deleted file mode 100644
index c01c018..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png
deleted file mode 100644
index 343e4ca..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png
deleted file mode 100644
index c3e2415..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png
deleted file mode 100644
index b8e399d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png
deleted file mode 100644
index 02b4e9a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/status_bar_latest_event.xml b/packages/SystemUI/res/layout/status_bar_latest_event.xml
index 59cc90d..88d9739 100644
--- a/core/res/res/layout/status_bar_latest_event.xml
+++ b/packages/SystemUI/res/layout/status_bar_latest_event.xml
@@ -4,20 +4,20 @@
android:orientation="vertical"
>
- <com.android.server.status.LatestItemView android:id="@+id/content"
+ <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="64sp"
- android:background="@drawable/status_bar_item_background"
+ android:background="@android:drawable/status_bar_item_background"
android:focusable="true"
android:clickable="true"
android:paddingRight="6sp"
>
- </com.android.server.status.LatestItemView>
+ </com.android.systemui.statusbar.LatestItemView>
<View
android:layout_width="match_parent"
android:layout_height="1sp"
- android:background="@drawable/divider_horizontal_bright"
+ android:background="@android:drawable/divider_horizontal_bright"
/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
deleted file mode 100644
index dbb0e0f..0000000
--- a/packages/SystemUI/res/values/arrays.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/colors.xml
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
- icons in the status bar that are not notifications. -->
- <string-array name="status_bar_icon_order">
- <item><xliff:g id="id">TODO: Remove; not used.</xliff:g></item>
- </string-array>
-
-</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index e9ae69a..a7e5e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -85,13 +85,6 @@ public class PhoneStatusBarService extends StatusBarService {
private static final int MSG_ANIMATE = 1000;
private static final int MSG_ANIMATE_REVEAL = 1001;
- public interface NotificationCallbacks {
- void onSetDisabled(int status);
- void onClearAll();
- void onNotificationClick(String pkg, String tag, int id);
- void onPanelRevealed();
- }
-
private class ExpandedDialog extends Dialog {
ExpandedDialog(Context context) {
super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
@@ -123,7 +116,6 @@ public class PhoneStatusBarService extends StatusBarService {
Object mQueueLock = new Object();
// icons
- String[] mRightIconSlots;
LinearLayout mIcons;
IconMerger mNotificationIcons;
LinearLayout mStatusIcons;
@@ -160,6 +152,7 @@ public class PhoneStatusBarService extends StatusBarService {
TrackingView mTrackingView;
WindowManager.LayoutParams mTrackingParams;
int mTrackingPosition; // the position of the top of the tracking view.
+ private boolean mPanelSlightlyVisible;
// ticker
private Ticker mTicker;
@@ -208,7 +201,6 @@ public class PhoneStatusBarService extends StatusBarService {
// ================================================================================
private void makeStatusBarView(Context context) {
Resources res = context.getResources();
- mRightIconSlots = res.getStringArray(R.array.status_bar_icon_order);
mHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mIconWidth = mHeight;
@@ -349,6 +341,16 @@ public class PhoneStatusBarService extends StatusBarService {
final RemoteViews contentView = notification.notification.contentView;
+ if (false) {
+ Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " expanded=" + oldEntry.expanded
+ + " contentView=" + oldContentView);
+ Slog.d(TAG, "new notification: when=" + notification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " contentView=" + contentView);
+ }
+
// Can we just reapply the RemoteViews in place? If when didn't change, the order
// didn't change.
if (notification.notification.when == oldNotification.notification.when
@@ -370,6 +372,14 @@ public class PhoneStatusBarService extends StatusBarService {
oldEntry.content.setOnClickListener(new Launcher(contentIntent,
notification.pkg, notification.tag, notification.id));
}
+ // Update the icon.
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+ notification.notification.icon, notification.notification.iconLevel,
+ notification.notification.number);
+ if (!oldEntry.icon.set(ic)) {
+ handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+ return;
+ }
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
@@ -377,9 +387,6 @@ public class PhoneStatusBarService extends StatusBarService {
removeNotificationViews(key);
addNotificationViews(key, notification);
}
- // Update the icon.
- oldEntry.icon.set(new StatusBarIcon(notification.pkg, notification.notification.icon,
- notification.notification.iconLevel, notification.notification.number));
} else {
Slog.d(TAG, "not reusing notification");
removeNotificationViews(key);
@@ -426,11 +433,10 @@ public class PhoneStatusBarService extends StatusBarService {
// create the row view
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event,
- parent, false);
+ View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
// bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
content.setOnFocusChangeListener(mFocusChangeListener);
PendingIntent contentIntent = n.contentIntent;
@@ -448,8 +454,9 @@ public class PhoneStatusBarService extends StatusBarService {
exception = e;
}
if (expanded == null) {
- Slog.e(TAG, "couldn't inflate view for package " + notification.pkg, exception);
- row.setVisibility(View.GONE);
+ String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident);
+ return null;
} else {
content.addView(expanded);
row.setDrawingCacheEnabled(true);
@@ -471,14 +478,23 @@ public class PhoneStatusBarService extends StatusBarService {
}
// Construct the expanded view.
final View[] views = makeNotificationView(notification, parent);
+ if (views == null) {
+ handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ + notification);
+ return;
+ }
final View row = views[0];
final View content = views[1];
final View expanded = views[2];
// Construct the icon.
- StatusBarIconView iconView = new StatusBarIconView(this,
+ final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
- iconView.set(new StatusBarIcon(notification.pkg, notification.notification.icon,
- notification.notification.iconLevel, notification.notification.number));
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
+ notification.notification.iconLevel, notification.notification.number);
+ if (!iconView.set(ic)) {
+ handleNotificationError(key, notification, "Coulding create icon: " + ic);
+ return;
+ }
// Add the expanded view.
final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
parent.addView(row, viewIndex);
@@ -964,6 +980,21 @@ public class PhoneStatusBarService extends StatusBarService {
}
}
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
+ void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+ removeNotification(key);
+ try {
+ mBarService.onNotificationError(n.pkg, n.tag, n.id, message);
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
private class MyTicker extends Ticker {
MyTicker(Context context, StatusBarView sb) {
super(context, sb);
@@ -1265,8 +1296,15 @@ public class PhoneStatusBarService extends StatusBarService {
// because the window itself extends below the content view.
mExpandedParams.y = -disph;
}
- visibilityChanged(visible);
mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+
+ // As long as this isn't just a repositioning that's not supposed to affect
+ // the user's perception of what's showing, call to say that the visibility
+ // has changed. (Otherwise, someone else will call to do that).
+ if (expandedPosition != EXPANDED_LEAVE_ALONE) {
+ Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
+ visibilityChanged(visible);
+ }
}
if (SPEW) {
@@ -1296,12 +1334,11 @@ public class PhoneStatusBarService extends StatusBarService {
* turned off. If any other notifications happen, the lights will turn back on. Steve says
* this is what he wants. (see bug 1131461)
*/
- private boolean mPanelSlightlyVisible;
void visibilityChanged(boolean visible) {
if (mPanelSlightlyVisible != visible) {
mPanelSlightlyVisible = visible;
try {
- mBarService.visibilityChanged(visible);
+ mBarService.onPanelRevealed();
} catch (RemoteException ex) {
// Won't fail unless the world has ended.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 5eb0d68..bc1e798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -32,7 +32,6 @@ public class StatusBarIconView extends AnimatedImageView {
private StatusBarIcon mIcon;
@ViewDebug.ExportedProperty private String mSlot;
- @ViewDebug.ExportedProperty private boolean mError;
public StatusBarIconView(Context context, String slot) {
super(context);
@@ -52,39 +51,33 @@ public class StatusBarIconView extends AnimatedImageView {
return a.equals(b);
}
- public void set(StatusBarIcon icon) {
- error: {
- final boolean iconEquals = !mError
- && mIcon != null
- && streq(mIcon.iconPackage, icon.iconPackage)
- && mIcon.iconId == icon.iconId;
- final boolean levelEquals = !mError
- && iconEquals
- && mIcon.iconLevel == icon.iconLevel;
- final boolean visibilityEquals = !mError
- && mIcon != null
- && mIcon.visible == icon.visible;
- mError = false;
- if (!iconEquals) {
- Drawable drawable = getIcon(icon);
- if (drawable == null) {
- mError = true;
- Slog.w(PhoneStatusBarService.TAG, "No icon ID for slot " + mSlot);
- break error;
- }
- setImageDrawable(drawable);
- }
- if (!levelEquals) {
- setImageLevel(icon.iconLevel);
- }
- if (!visibilityEquals) {
- setVisibility(icon.visible ? VISIBLE : GONE);
+ /**
+ * Returns whether the set succeeded.
+ */
+ public boolean set(StatusBarIcon icon) {
+ final boolean iconEquals = mIcon != null
+ && streq(mIcon.iconPackage, icon.iconPackage)
+ && mIcon.iconId == icon.iconId;
+ final boolean levelEquals = iconEquals
+ && mIcon.iconLevel == icon.iconLevel;
+ final boolean visibilityEquals = mIcon != null
+ && mIcon.visible == icon.visible;
+ if (!iconEquals) {
+ Drawable drawable = getIcon(icon);
+ if (drawable == null) {
+ Slog.w(PhoneStatusBarService.TAG, "No icon for slot " + mSlot);
+ return false;
}
- mIcon = icon.clone();
+ setImageDrawable(drawable);
+ }
+ if (!levelEquals) {
+ setImageLevel(icon.iconLevel);
}
- if (mError) {
- setVisibility(GONE);
+ if (!visibilityEquals) {
+ setVisibility(icon.visible ? VISIBLE : GONE);
}
+ mIcon = icon.clone();
+ return true;
}
private Drawable getIcon(StatusBarIcon icon) {
@@ -106,7 +99,7 @@ public class StatusBarIconView extends AnimatedImageView {
try {
r = context.getPackageManager().getResourcesForApplication(icon.iconPackage);
} catch (PackageManager.NameNotFoundException ex) {
- Slog.e(PhoneStatusBarService.TAG, "Icon package not found: "+icon.iconPackage, ex);
+ Slog.e(PhoneStatusBarService.TAG, "Icon package not found: " + icon.iconPackage);
return null;
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 9ef9d0d..4f39ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -365,7 +365,7 @@ public class StatusBarPolicy {
// storage
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
mStorageManager.registerListener(
- new com.android.server.status.StorageNotification(context));
+ new com.android.systemui.usb.StorageNotification(context));
// battery
mService.setIcon("battery", com.android.internal.R.drawable.stat_sys_battery_unknown, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index 4d9e695..d200886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -48,12 +48,6 @@ public abstract class StatusBarService extends Service implements CommandQueue.C
CommandQueue mCommandQueue;
IStatusBarService mBarService;
- /* TODO
- H mHandler = new H();
- Object mQueueLock = new Object();
- NotificationCallbacks mNotificationCallbacks;
- */
-
@Override
public void onCreate() {
// Connect in to the status bar manager service
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java
deleted file mode 100644
index 2b9dfb0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.util.Log;
-
-/**
- * Receive a broadcast from the StatusBarManagerService at boot time, and
- * kick off the StatusBarService.
- */
-public class StatusBarStarter extends BroadcastReceiver {
- private static final String TAG = "StatusBarStarter";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "StatusBarStarter onReceive intent=" + intent);
- context.startService(new Intent(context, PhoneStatusBarService.class));
- }
-}
-
-
diff --git a/services/java/com/android/server/status/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 8da8cd3..f8abc5a 100644
--- a/services/java/com/android/server/status/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.usb;
import android.app.Activity;
import android.app.Notification;
@@ -80,9 +80,10 @@ public class StorageNotification extends StorageEventListener {
mContext = context;
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
+ final boolean connected = mStorageManager.isUsbMassStorageConnected();
Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
Environment.getExternalStorageState()));
+ onUsbMassStorageConnectionChanged(connected);
}
/*
@@ -122,7 +123,7 @@ public class StorageNotification extends StorageEventListener {
* for stopping UMS.
*/
Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
+ intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
setUsbStorageNotification(
com.android.internal.R.string.usb_storage_stop_notification_title,
@@ -240,7 +241,7 @@ public class StorageNotification extends StorageEventListener {
if (available) {
Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
+ intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final boolean adbOn = 1 == Settings.Secure.getInt(
diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index e8631c5..55d31ec 100644
--- a/services/java/com/android/server/status/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.usb;
import com.android.internal.R;
import android.app.Activity;
diff --git a/policy/com/android/internal/policy/impl/PhoneWindow.java b/policy/com/android/internal/policy/impl/PhoneWindow.java
index 0bf0d52..1c78a01 100644
--- a/policy/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/com/android/internal/policy/impl/PhoneWindow.java
@@ -321,9 +321,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (st.createdPanelView == null) {
// Init the panel state's menu--return false if init failed
- if (st.menu == null) {
- if (!initializePanelMenu(st) || (st.menu == null)) {
- return false;
+ if (st.menu == null || st.refreshMenuContent) {
+ if (st.menu == null) {
+ if (!initializePanelMenu(st) || (st.menu == null)) {
+ return false;
+ }
}
// Call callback, and return if it doesn't want to display menu
if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
@@ -332,6 +334,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return false;
}
+ st.refreshMenuContent = false;
}
// Callback and return if the callback does not want to show the menu
@@ -551,6 +554,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
+ @Override
+ public void invalidatePanelMenu(int featureId) {
+ PanelFeatureState st = getPanelState(featureId, true);
+ if (st.menu != null) {
+ st.menu.clear();
+ }
+ st.refreshMenuContent = true;
+ st.refreshDecorView = true;
+ }
+
/**
* Called when the panel key is pushed down.
* @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
@@ -2640,6 +2653,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean refreshDecorView;
+ boolean refreshMenuContent;
+
boolean wasLastOpen;
boolean wasLastExpanded;
diff --git a/policy/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
index 73a57ee..d152bc4 100755
--- a/policy/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -957,12 +957,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_STATUS_BAR:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "PhoneWindowManager");
+ // TODO: Need to handle the race condition of the status bar proc
+ // dying and coming back before the removeWindowLw cleanup has happened.
if (mStatusBar != null) {
return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
}
mStatusBar = win;
break;
case TYPE_STATUS_BAR_PANEL:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "PhoneWindowManager");
mStatusBarPanels.add(win);
break;
case TYPE_KEYGUARD:
diff --git a/services/java/com/android/server/DemoDataSet.java b/services/java/com/android/server/DemoDataSet.java
deleted file mode 100644
index 277985f..0000000
--- a/services/java/com/android/server/DemoDataSet.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.AssetManager;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.Contacts;
-import android.provider.Settings;
-import android.provider.MediaStore.Images;
-import android.util.Config;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class DemoDataSet
-{
- private final static String LOG_TAG = "DemoDataSet";
-
- private ContentResolver mContentResolver;
-
- public final void add(Context context)
- {
- mContentResolver = context.getContentResolver();
-
- // Remove all the old data
- mContentResolver.delete(Contacts.People.CONTENT_URI, null, null);
-
- // Add the new data
- addDefaultData();
-
- // Add images from /android/images
- addDefaultImages();
- }
-
- private final void addDefaultImages()
- {
- File rootDirectory = Environment.getRootDirectory();
- String [] files
- = new File(rootDirectory, "images").list();
- int count = files.length;
-
- if (count == 0) {
- Slog.i(LOG_TAG, "addDefaultImages: no images found!");
- return;
- }
-
- for (int i = 0; i < count; i++)
- {
- String name = files[i];
- String path = rootDirectory + "/" + name;
-
- try {
- Images.Media.insertImage(mContentResolver, path, name, null);
- } catch (FileNotFoundException e) {
- Slog.e(LOG_TAG, "Failed to import image " + path, e);
- }
- }
- }
-
- private final void addDefaultData()
- {
- Slog.i(LOG_TAG, "Adding default data...");
-
-// addImage("Violet", "images/violet.png");
-// addImage("Corky", "images/corky.png");
-
- // PENDING: should this be done here?!?!
- Intent intent = new Intent(
- Intent.ACTION_CALL, Uri.fromParts("voicemail", "", null));
- addShortcut("1", intent);
- }
-
- private final Uri addImage(String name, Uri file)
- {
- ContentValues imagev = new ContentValues();
- imagev.put("name", name);
-
- Uri url = null;
-
- AssetManager ass = AssetManager.getSystem();
- InputStream in = null;
- OutputStream out = null;
-
- try
- {
- in = ass.open(file.toString());
-
- url = mContentResolver.insert(Images.Media.INTERNAL_CONTENT_URI, imagev);
- out = mContentResolver.openOutputStream(url);
-
- final int size = 8 * 1024;
- byte[] buf = new byte[size];
-
- int count = 0;
- do
- {
- count = in.read(buf, 0, size);
- if (count > 0) {
- out.write(buf, 0, count);
- }
- } while (count > 0);
- }
- catch (Exception e)
- {
- Slog.e(LOG_TAG, "Failed to insert image '" + file + "'", e);
- url = null;
- }
-
- return url;
- }
-
- private final Uri addShortcut(String shortcut, Intent intent)
- {
- if (Config.LOGV) Slog.v(LOG_TAG, "addShortcut: shortcut=" + shortcut + ", intent=" + intent);
- return Settings.Bookmarks.add(mContentResolver, intent, null, null,
- shortcut != null ? shortcut.charAt(0) : 0, 0);
- }
-}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index d2add10..c0ea68d 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -80,6 +80,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int mActivePasswordLetters = 0;
int mActivePasswordNumeric = 0;
int mActivePasswordSymbols = 0;
+ int mActivePasswordNonLetter = 0;
int mFailedPasswordAttempts = 0;
int mPasswordOwner = -1;
@@ -100,6 +101,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int minimumPasswordLetters = 1;
int minimumPasswordNumeric = 1;
int minimumPasswordSymbols = 1;
+ int minimumPasswordNonLetter = 0;
long maximumTimeToUnlock = 0;
int maximumFailedPasswordsForWipe = 0;
@@ -153,6 +155,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
out.endTag(null, "min-password-symbols");
}
+ if (minimumPasswordNonLetter > 0) {
+ out.startTag(null, "min-password-nonletter");
+ out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
+ out.endTag(null, "min-password-nonletter");
+ }
}
if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
out.startTag(null, "max-time-to-unlock");
@@ -202,6 +209,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if ("min-password-symbols".equals(tag)) {
minimumPasswordSymbols = Integer.parseInt(
parser.getAttributeValue(null, "value"));
+ } else if ("min-password-nonletter".equals(tag)) {
+ minimumPasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
} else if ("max-time-to-unlock".equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
parser.getAttributeValue(null, "value"));
@@ -240,6 +250,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.println(minimumPasswordNumeric);
pw.print(prefix); pw.print("minimumPasswordSymbols=");
pw.println(minimumPasswordSymbols);
+ pw.print(prefix); pw.print("minimumPasswordNonLetter=");
+ pw.println(minimumPasswordNonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
@@ -429,7 +441,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
|| mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
|| mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
- || mActivePasswordSymbols != 0) {
+ || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
out.startTag(null, "active-password");
out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
out.attribute(null, "length", Integer.toString(mActivePasswordLength));
@@ -439,6 +451,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "numeric", Integer
.toString(mActivePasswordNumeric));
out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
+ out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
out.endTag(null, "active-password");
}
@@ -447,6 +460,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endDocument();
stream.close();
journal.commit();
+ sendChangedNotification();
} catch (IOException e) {
try {
if (stream != null) {
@@ -459,6 +473,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private void sendChangedNotification() {
+ Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcast(intent);
+ }
+
private void loadSettingsLocked() {
JournaledFile journal = makeJournaledFile();
FileInputStream stream = null;
@@ -522,6 +542,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
parser.getAttributeValue(null, "numeric"));
mActivePasswordSymbols = Integer.parseInt(
parser.getAttributeValue(null, "symbols"));
+ mActivePasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown tag: " + tag);
@@ -564,6 +586,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mActivePasswordLetters = 0;
mActivePasswordNumeric = 0;
mActivePasswordSymbols = 0;
+ mActivePasswordNonLetter = 0;
}
validatePasswordOwnerLocked();
@@ -967,6 +990,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ public void setPasswordMinimumNonLetter(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNonLetter != length) {
+ ap.minimumPasswordNonLetter = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumNonLetter(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordNonLetter : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordNonLetter) {
+ length = admin.minimumPasswordNonLetter;
+ }
+ }
+ return length;
+ }
+ }
+
public boolean isActivePasswordSufficient() {
synchronized (this) {
// This API can only be called by an active device admin,
@@ -984,7 +1041,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
&& mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null)
&& mActivePasswordLetters >= getPasswordMinimumLetters(null)
&& mActivePasswordNumeric >= getPasswordMinimumNumeric(null)
- && mActivePasswordSymbols >= getPasswordMinimumSymbols(null);
+ && mActivePasswordSymbols >= getPasswordMinimumSymbols(null)
+ && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null);
}
}
@@ -1068,6 +1126,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int lowercase = 0;
int numbers = 0;
int symbols = 0;
+ int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
if (c >= 'A' && c <= 'Z') {
@@ -1078,8 +1137,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
lowercase++;
} else if (c >= '0' && c <= '9') {
numbers++;
+ nonletter++;
} else {
symbols++;
+ nonletter++;
}
}
int neededLetters = getPasswordMinimumLetters(null);
@@ -1116,6 +1177,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ " does not meet required number of special symbols " + neededSymbols);
return false;
}
+ int neededNonLetter = getPasswordMinimumNonLetter(null);
+ if (nonletter < neededNonLetter) {
+ Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter
+ + " does not meet required number of non-letter characters "
+ + neededNonLetter);
+ return false;
+ }
}
LockPatternUtils utils = new LockPatternUtils(mContext);
@@ -1275,7 +1343,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
public void setActivePasswordState(int quality, int length, int letters, int uppercase,
- int lowercase, int numbers, int symbols) {
+ int lowercase, int numbers, int symbols, int nonletter) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -1286,7 +1354,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters
|| mActivePasswordUpperCase != uppercase
|| mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers
- || mActivePasswordSymbols != symbols) {
+ || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) {
long ident = Binder.clearCallingIdentity();
try {
mActivePasswordQuality = quality;
@@ -1296,6 +1364,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mActivePasswordUpperCase = uppercase;
mActivePasswordNumeric = numbers;
mActivePasswordSymbols = symbols;
+ mActivePasswordNonLetter = nonletter;
mFailedPasswordAttempts = 0;
saveSettingsLocked();
sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
@@ -1384,6 +1453,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.print(" mActivePasswordLetters="); pw.println(mActivePasswordLetters);
pw.print(" mActivePasswordNumeric="); pw.println(mActivePasswordNumeric);
pw.print(" mActivePasswordSymbols="); pw.println(mActivePasswordSymbols);
+ pw.print(" mActivePasswordNonLetter="); pw.println(mActivePasswordNonLetter);
pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 4b86135..f6e3441 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -26,7 +26,7 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.status.StatusBarManagerService;
+import com.android.server.StatusBarManagerService;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index ac3b23b..6f44e8e 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -17,7 +17,7 @@
package com.android.server;
import com.android.internal.statusbar.StatusBarNotification;
-import com.android.server.status.StatusBarManagerService;
+import com.android.server.StatusBarManagerService;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -70,6 +70,8 @@ class NotificationManagerService extends INotificationManager.Stub
private static final String TAG = "NotificationService";
private static final boolean DBG = false;
+ private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
+
// message codes
private static final int MESSAGE_TIMEOUT = 2;
@@ -301,6 +303,12 @@ class NotificationManagerService extends INotificationManager.Stub
updateLightsLocked();
}
}
+
+ public void onNotificationError(String pkg, String tag, int id, String message) {
+ Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id);
+ cancelNotification(pkg, tag, id, 0, 0);
+ // TODO: Tell the activity manager.
+ }
};
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -657,6 +665,26 @@ class NotificationManagerService extends INotificationManager.Stub
{
checkIncomingCall(pkg);
+ // Limit the number of notifications that any given package except the android
+ // package can enqueue. Prevents DOS attacks and deals with leaks.
+ if (!"android".equals(pkg)) {
+ synchronized (mNotificationList) {
+ int count = 0;
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ final NotificationRecord r = mNotificationList.get(i);
+ if (r.pkg.equals(pkg)) {
+ count++;
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ Slog.e(TAG, "Package has already posted " + count
+ + " notifications. Not showing more. package=" + pkg);
+ return;
+ }
+ }
+ }
+ }
+ }
+
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
diff --git a/services/java/com/android/server/status/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 0af1ebb..1a16387 100644
--- a/services/java/com/android/server/status/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.server;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -55,9 +56,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
static final String TAG = "StatusBarManagerService";
static final boolean SPEW = true;
- public static final String ACTION_STATUSBAR_START
- = "com.android.internal.policy.statusbar.START";
-
final Context mContext;
Handler mHandler = new Handler();
NotificationCallbacks mNotificationCallbacks;
@@ -87,6 +85,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub
void onClearAll();
void onNotificationClick(String pkg, String tag, int id);
void onPanelRevealed();
+ void onNotificationError(String pkg, String tag, int id, String message);
}
/**
@@ -96,7 +95,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub
mContext = context;
final Resources res = context.getResources();
- mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.status_bar_icon_order));
+ mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
}
public void setNotificationCallbacks(NotificationCallbacks listener) {
@@ -111,9 +110,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
public void systemReady2() {
- // Start the status bar app
- Intent intent = new Intent(ACTION_STATUSBAR_START);
- mContext.sendBroadcast(intent /** permission **/);
+ ComponentName cn = ComponentName.unflattenFromString(
+ mContext.getString(com.android.internal.R.string.config_statusBarComponent));
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ Slog.i(TAG, "Starting service: " + cn);
+ mContext.startService(intent);
}
// ================================================================================
@@ -248,12 +250,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub
"StatusBarManagerService");
}
+ private void enforceStatusBarService() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ "StatusBarManagerService");
+ }
+
// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+ enforceStatusBarService();
+
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
synchronized (mIcons) {
@@ -268,18 +277,32 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
/**
- * The status bar service should call this when the user changes whether
- * the status bar is visible or not.
+ * The status bar service should call this each time the user brings the panel from
+ * invisible to visible in order to clear the notification light.
*/
- public void visibilityChanged(boolean visible) {
- //Slog.d(TAG, "visibilityChanged visible=" + visible);
+ public void onPanelRevealed() {
+ enforceStatusBarService();
+
+ // tell the notification manager to turn off the lights.
+ mNotificationCallbacks.onPanelRevealed();
}
public void onNotificationClick(String pkg, String tag, int id) {
+ enforceStatusBarService();
+
mNotificationCallbacks.onNotificationClick(pkg, tag, id);
}
+ public void onNotificationError(String pkg, String tag, int id, String message) {
+ enforceStatusBarService();
+
+ // WARNING: this will call back into us to do the remove. Don't hold any locks.
+ mNotificationCallbacks.onNotificationError(pkg, tag, id, message);
+ }
+
public void onClearAllNotifications() {
+ enforceStatusBarService();
+
mNotificationCallbacks.onClearAll();
}
@@ -423,24 +446,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
}
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- private boolean mPanelSlightlyVisible;
- void panelSlightlyVisible(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- if (visible) {
- // tell the notification manager to turn off the lights.
- mNotificationCallbacks.onPanelRevealed();
- }
- }
- }
-
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4307cdc..e7b8c02 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,33 +17,32 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
-import com.android.server.status.StatusBarManagerService;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
+import android.accounts.AccountManagerService;
import android.app.ActivityManagerNative;
import android.bluetooth.BluetoothAdapter;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentService;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.IPackageManager;
import android.database.ContentObserver;
-import android.database.Cursor;
import android.media.AudioService;
-import android.os.*;
-import android.provider.Contacts.People;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.EventLog;
import android.util.Slog;
-import android.accounts.AccountManagerService;
import java.io.File;
import java.util.Timer;
@@ -51,11 +50,8 @@ import java.util.TimerTask;
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
- private final static boolean INCLUDE_DEMO = false;
-
- private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010;
- private ContentResolver mContentResolver;
+ ContentResolver mContentResolver;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -329,11 +325,6 @@ class ServerThread extends Thread {
Slog.e(TAG, "Failure starting Search Service", e);
}
- if (INCLUDE_DEMO) {
- Slog.i(TAG, "Installing demo data...");
- (new DemoThread(context)).start();
- }
-
try {
Slog.i(TAG, "DropBox Service");
ServiceManager.addService(Context.DROPBOX_SERVICE,
@@ -504,37 +495,7 @@ class ServerThread extends Thread {
}
}
-class DemoThread extends Thread
-{
- DemoThread(Context context)
- {
- mContext = context;
- }
-
- @Override
- public void run()
- {
- try {
- Cursor c = mContext.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
- boolean hasData = c != null && c.moveToFirst();
- if (c != null) {
- c.deactivate();
- }
- if (!hasData) {
- DemoDataSet dataset = new DemoDataSet();
- dataset.add(mContext);
- }
- } catch (Throwable e) {
- Slog.e("SystemServer", "Failure installing demo data", e);
- }
-
- }
-
- Context mContext;
-}
-
-public class SystemServer
-{
+public class SystemServer {
private static final String TAG = "SystemServer";
public static final int FACTORY_TEST_OFF = 0;
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 87f8a6e..445dd03 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -1217,8 +1217,9 @@ public class WifiWatchdogService {
private static Random sRandom = new Random();
static boolean isDnsReachable(int dns, int timeout) {
+ DatagramSocket socket = null;
try {
- DatagramSocket socket = new DatagramSocket();
+ socket = new DatagramSocket();
// Set some socket properties
socket.setSoTimeout(timeout);
@@ -1271,6 +1272,10 @@ public class WifiWatchdogService {
Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e);
}
return false;
+ } finally {
+ if (socket != null) {
+ socket.close();
+ }
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index c828a7e..97d0f9b 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -55,7 +55,10 @@ import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
@@ -234,11 +237,20 @@ public class WindowManagerService extends IWindowManager.Stub
*/
private boolean mKeyguardDisabled = false;
+ private static final int ALLOW_DISABLE_YES = 1;
+ private static final int ALLOW_DISABLE_NO = 0;
+ private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
+ private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher
+
final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher(
new Handler(), "WindowManagerService.mKeyguardTokenWatcher") {
public void acquired() {
- mPolicy.enableKeyguard(false);
- mKeyguardDisabled = true;
+ if (shouldAllowDisableKeyguard()) {
+ mPolicy.enableKeyguard(false);
+ mKeyguardDisabled = true;
+ } else {
+ Log.v(TAG, "Not disabling keyguard since device policy is enforced");
+ }
}
public void released() {
mPolicy.enableKeyguard(true);
@@ -249,6 +261,18 @@ public class WindowManagerService extends IWindowManager.Stub
}
};
+ final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mPolicy.enableKeyguard(true);
+ synchronized(mKeyguardTokenWatcher) {
+ // lazily evaluate this next time we're asked to disable keyguard
+ mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
+ mKeyguardDisabled = false;
+ }
+ }
+ };
+
final Context mContext;
final boolean mHaveInputMethods;
@@ -609,6 +633,11 @@ public class WindowManagerService extends IWindowManager.Stub
mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
int max_events_per_sec = 35;
try {
max_events_per_sec = Integer.parseInt(SystemProperties
@@ -4172,17 +4201,20 @@ public class WindowManagerService extends IWindowManager.Stub
// Misc IWindowSession methods
// -------------------------------------------------------------
- private boolean allowDisableKeyguard()
+ private boolean shouldAllowDisableKeyguard()
{
- // We fail safe if this gets called before the service has started.
- boolean allow = false;
- DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- allow = dpm.getPasswordQuality(null)
- == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ // We fail safe and prevent disabling keyguard in the unlikely event this gets
+ // called before DevicePolicyManagerService has started.
+ if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ mAllowDisableKeyguard = dpm.getPasswordQuality(null)
+ == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
+ ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
+ }
}
- return allow;
+ return mAllowDisableKeyguard == ALLOW_DISABLE_YES;
}
public void disableKeyguard(IBinder token, String tag) {
@@ -4191,12 +4223,8 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- if (allowDisableKeyguard()) {
- synchronized (mKeyguardTokenWatcher) {
- mKeyguardTokenWatcher.acquire(token, tag);
- }
- } else {
- Log.w(TAG, tag + ": disableKeyguard() ignored while DevicePolicyAmin is enabled.");
+ synchronized (mKeyguardTokenWatcher) {
+ mKeyguardTokenWatcher.acquire(token, tag);
}
}
@@ -4206,29 +4234,25 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- if (allowDisableKeyguard()) {
- synchronized (mKeyguardTokenWatcher) {
- mKeyguardTokenWatcher.release(token);
-
- if (!mKeyguardTokenWatcher.isAcquired()) {
- // If we are the last one to reenable the keyguard wait until
- // we have actaully finished reenabling until returning.
- // It is possible that reenableKeyguard() can be called before
- // the previous disableKeyguard() is handled, in which case
- // neither mKeyguardTokenWatcher.acquired() or released() would
- // be called. In that case mKeyguardDisabled will be false here
- // and we have nothing to wait for.
- while (mKeyguardDisabled) {
- try {
- mKeyguardTokenWatcher.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
+ synchronized (mKeyguardTokenWatcher) {
+ mKeyguardTokenWatcher.release(token);
+
+ if (!mKeyguardTokenWatcher.isAcquired()) {
+ // If we are the last one to reenable the keyguard wait until
+ // we have actually finished reenabling until returning.
+ // It is possible that reenableKeyguard() can be called before
+ // the previous disableKeyguard() is handled, in which case
+ // neither mKeyguardTokenWatcher.acquired() or released() would
+ // be called. In that case mKeyguardDisabled will be false here
+ // and we have nothing to wait for.
+ while (mKeyguardDisabled) {
+ try {
+ mKeyguardTokenWatcher.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
}
}
- } else {
- Log.w(TAG, "reenableKeyguard() ignored while DevicePolicyAmin is enabled.");
}
}
diff --git a/services/java/com/android/server/status/LatestItemView.java b/services/java/com/android/server/status/LatestItemView.java
deleted file mode 100644
index fe8d164..0000000
--- a/services/java/com/android/server/status/LatestItemView.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-public class LatestItemView extends FrameLayout {
-
- public LatestItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public boolean dispatchTouchEvent(MotionEvent ev) {
- return onTouchEvent(ev);
- }
-}
diff --git a/services/java/com/android/server/status/package.html b/services/java/com/android/server/status/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/services/java/com/android/server/status/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 3602fec..4d071e3 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -44,6 +44,7 @@ public class NotificationTestList extends TestActivity
Vibrator mVibrator = new Vibrator();
Handler mHandler = new Handler();
+ long mActivityCreateTime = System.currentTimeMillis();
long mChronometerBase = 0;
@Override
@@ -122,29 +123,49 @@ public class NotificationTestList extends TestActivity
}
},
- new Test("Bad Icon") {
+ new Test("Bad Icon #1 (when=create)") {
public void run() {
- mNM.notify(1, new Notification(NotificationTestList.this,
- R.layout.chrono_notification, /* not a drawable! */
- null, System.currentTimeMillis()-(1000*60*60*24),
- "(453) 123-2328",
- "", null));
+ Notification n = new Notification(R.layout.chrono_notification /* not an icon */,
+ null, mActivityCreateTime);
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+ "This is the same notification!!!", makeIntent());
+ mNM.notify(1, n);
}
},
- new Test("Bad resource #2") {
- public void run()
- {
- Notification n = new Notification(NotificationTestList.this,
- R.drawable.ic_statusbar_missedcall,
- null, System.currentTimeMillis()-(1000*60*60*24),
- "(453) 123-2328",
- "", null);
+ new Test("Bad Icon #1 (when=now)") {
+ public void run() {
+ Notification n = new Notification(R.layout.chrono_notification /* not an icon */,
+ null, System.currentTimeMillis());
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+ "This is the same notification!!!", makeIntent());
+ mNM.notify(1, n);
+ }
+ },
+
+ new Test("Bad resource #1 (when=create)") {
+ public void run() {
+ Notification n = new Notification(R.drawable.icon2,
+ null, mActivityCreateTime);
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+ "This is the same notification!!!", makeIntent());
n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
- mNM.notify(2, n);
+ mNM.notify(1, n);
}
},
+ new Test("Bad resource #1 (when=now)") {
+ public void run() {
+ Notification n = new Notification(R.drawable.icon2,
+ null, System.currentTimeMillis());
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+ "This is the same notification!!!", makeIntent());
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(1, n);
+ }
+ },
+
+
new Test("Bad resource #3") {
public void run()
{
@@ -421,7 +442,7 @@ public class NotificationTestList extends TestActivity
new Test("Persistent #1") {
public void run() {
Notification n = new Notification(R.drawable.icon1, "tick tick tick",
- System.currentTimeMillis());
+ mActivityCreateTime);
n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
"This is a notification!!!", makeIntent());
mNM.notify(1, n);
@@ -481,6 +502,16 @@ public class NotificationTestList extends TestActivity
}
},
+ new Test("Persistent #1 - different icon") {
+ public void run() {
+ Notification n = new Notification(R.drawable.icon2, null,
+ mActivityCreateTime);
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+ "This is the same notification!!!", makeIntent());
+ mNM.notify(1, n);
+ }
+ },
+
new Test("Chronometer Start") {
public void run() {
Notification n = new Notification(R.drawable.icon2, "me me me me",
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 3532e30..cb94e52 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -157,5 +157,22 @@ public class StatusBarTest extends TestActivity
}, 3000);
}
},
+ new Test("More icons") {
+ public void run() {
+ for (String slot: new String[] {
+ "sync_failing",
+ "gps",
+ "bluetooth",
+ "tty",
+ "speakerphone",
+ "mute",
+ "wifi",
+ "alarm_clock",
+ "secure",
+ }) {
+ mStatusBarManager.setIconVisibility(slot, true);
+ }
+ }
+ },
};
}