Merge "Clear interface address and reset connections" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index 1a6657c..e86de90 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -331,6 +331,7 @@
field public static final int checkboxStyle = 16842860; // 0x101006c
field public static final int checked = 16843014; // 0x1010106
field public static final int checkedButton = 16843080; // 0x1010148
+ field public static final int checkedTextViewStyle = 16843720; // 0x10103c8
field public static final int childDivider = 16843025; // 0x1010111
field public static final int childIndicator = 16843020; // 0x101010c
field public static final int childIndicatorLeft = 16843023; // 0x101010f
@@ -1789,6 +1790,7 @@
field public static final int Widget_DeviceDefault_Button_Small = 16974146; // 0x1030142
field public static final int Widget_DeviceDefault_Button_Toggle = 16974148; // 0x1030144
field public static final int Widget_DeviceDefault_CalendarView = 16974190; // 0x103016e
+ field public static final int Widget_DeviceDefault_CheckedTextView = 16974299; // 0x10301db
field public static final int Widget_DeviceDefault_CompoundButton_CheckBox = 16974152; // 0x1030148
field public static final int Widget_DeviceDefault_CompoundButton_RadioButton = 16974169; // 0x1030159
field public static final int Widget_DeviceDefault_CompoundButton_Star = 16974173; // 0x103015d
@@ -1822,6 +1824,7 @@
field public static final int Widget_DeviceDefault_Light_Button_Small = 16974198; // 0x1030176
field public static final int Widget_DeviceDefault_Light_Button_Toggle = 16974200; // 0x1030178
field public static final int Widget_DeviceDefault_Light_CalendarView = 16974238; // 0x103019e
+ field public static final int Widget_DeviceDefault_Light_CheckedTextView = 16974300; // 0x10301dc
field public static final int Widget_DeviceDefault_Light_CompoundButton_CheckBox = 16974204; // 0x103017c
field public static final int Widget_DeviceDefault_Light_CompoundButton_RadioButton = 16974224; // 0x1030190
field public static final int Widget_DeviceDefault_Light_CompoundButton_Star = 16974228; // 0x1030194
@@ -1907,6 +1910,7 @@
field public static final int Widget_Holo_Button_Small = 16973964; // 0x103008c
field public static final int Widget_Holo_Button_Toggle = 16973966; // 0x103008e
field public static final int Widget_Holo_CalendarView = 16974060; // 0x10300ec
+ field public static final int Widget_Holo_CheckedTextView = 16974297; // 0x10301d9
field public static final int Widget_Holo_CompoundButton_CheckBox = 16973969; // 0x1030091
field public static final int Widget_Holo_CompoundButton_RadioButton = 16973986; // 0x10300a2
field public static final int Widget_Holo_CompoundButton_Star = 16973990; // 0x10300a6
@@ -1940,6 +1944,7 @@
field public static final int Widget_Holo_Light_Button_Small = 16974007; // 0x10300b7
field public static final int Widget_Holo_Light_Button_Toggle = 16974009; // 0x10300b9
field public static final int Widget_Holo_Light_CalendarView = 16974061; // 0x10300ed
+ field public static final int Widget_Holo_Light_CheckedTextView = 16974298; // 0x10301da
field public static final int Widget_Holo_Light_CompoundButton_CheckBox = 16974012; // 0x10300bc
field public static final int Widget_Holo_Light_CompoundButton_RadioButton = 16974032; // 0x10300d0
field public static final int Widget_Holo_Light_CompoundButton_Star = 16974036; // 0x10300d4
@@ -2673,6 +2678,7 @@
method public void invalidateOptionsMenu();
method public boolean isChangingConfigurations();
method public final boolean isChild();
+ method public boolean isDestroyed();
method public boolean isFinishing();
method public boolean isTaskRoot();
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
@@ -3480,6 +3486,7 @@
method public abstract int getBackStackEntryCount();
method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
method public void invalidateOptionsMenu();
+ method public abstract boolean isDestroyed();
method public abstract void popBackStack();
method public abstract void popBackStack(java.lang.String, int);
method public abstract void popBackStack(int, int);
@@ -23714,6 +23721,7 @@
public final class Display {
method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
method public int getDisplayId();
+ method public int getFlags();
method public deprecated int getHeight();
method public void getMetrics(android.util.DisplayMetrics);
method public java.lang.String getName();
@@ -23728,6 +23736,7 @@
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
public class DragEvent implements android.os.Parcelable {
diff --git a/api/current.txt b/api/current.txt
index 4e7d012..e86de90 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2678,6 +2678,7 @@
method public void invalidateOptionsMenu();
method public boolean isChangingConfigurations();
method public final boolean isChild();
+ method public boolean isDestroyed();
method public boolean isFinishing();
method public boolean isTaskRoot();
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
@@ -3485,6 +3486,7 @@
method public abstract int getBackStackEntryCount();
method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
method public void invalidateOptionsMenu();
+ method public abstract boolean isDestroyed();
method public abstract void popBackStack();
method public abstract void popBackStack(java.lang.String, int);
method public abstract void popBackStack(int, int);
@@ -23719,6 +23721,7 @@
public final class Display {
method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
method public int getDisplayId();
+ method public int getFlags();
method public deprecated int getHeight();
method public void getMetrics(android.util.DisplayMetrics);
method public java.lang.String getName();
@@ -23733,6 +23736,7 @@
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
public class DragEvent implements android.os.Parcelable {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7606d5e3..5dc9da2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -685,6 +685,7 @@
private boolean mStopped;
boolean mFinished;
boolean mStartedActivity;
+ private boolean mDestroyed;
/** true if the activity is going through a transient pause */
/*package*/ boolean mTemporaryPause = false;
/** true if the activity is being destroyed in order to recreate it with a new configuration */
@@ -4089,6 +4090,14 @@
}
/**
+ * Returns true if the final {@link #onDestroy()} call has been made
+ * on the Activity, so this instance is now dead.
+ */
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ /**
* Check to see whether this activity is in the process of being destroyed in order to be
* recreated with a new configuration. This is often used in
* {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
@@ -5258,6 +5267,7 @@
}
final void performDestroy() {
+ mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c41405b..59fa1e0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1703,6 +1703,7 @@
if (packageName.equals("system") || packageName.equals("android")) {
final ContextImpl context = new ContextImpl(mMainThread.getSystemContext());
context.mBasePackageName = mBasePackageName;
+ context.mUser = user;
return context;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index e983299..10ea109 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -318,6 +318,12 @@
public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
/**
+ * Returns true if the final {@link Activity#onDestroy() Activity.onDestroy()}
+ * call has been made on the FragmentManager's Activity, so this instance is now dead.
+ */
+ public abstract boolean isDestroyed();
+
+ /**
* Print the FragmentManager's state into the given stream.
*
* @param prefix Text to print at the front of each line.
@@ -588,6 +594,11 @@
}
@Override
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("FragmentManager{");
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 7b3d8cd..3579977 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -34,14 +34,6 @@
public HashSet<String> enabledComponents;
public PackageUserState() {
- this(true);
- }
-
- /** @hide */
- public PackageUserState(boolean isSystem) {
- if (!isSystem) {
- stopped = notLaunched = true;
- }
installed = true;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 98bd4f5..8286686 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -44,12 +44,12 @@
/* Sets the default package for a USB device
* (or clears it if the package name is null)
*/
- void setDevicePackage(in UsbDevice device, String packageName);
+ void setDevicePackage(in UsbDevice device, String packageName, int userId);
/* Sets the default package for a USB accessory
* (or clears it if the package name is null)
*/
- void setAccessoryPackage(in UsbAccessory accessory, String packageName);
+ void setAccessoryPackage(in UsbAccessory accessory, String packageName, int userId);
/* Returns true if the caller has permission to access the device. */
boolean hasDevicePermission(in UsbDevice device);
@@ -77,10 +77,10 @@
void grantAccessoryPermission(in UsbAccessory accessory, int uid);
/* Returns true if the USB manager has default preferences or permissions for the package */
- boolean hasDefaults(String packageName);
+ boolean hasDefaults(String packageName, int userId);
/* Clears default preferences and permissions for the package */
- void clearDefaults(String packageName);
+ void clearDefaults(String packageName, int userId);
/* Sets the current USB function. */
void setCurrentFunction(String function, boolean makeDefault);
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 7b16f4d..2e38960 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -117,4 +117,8 @@
public static final int BATTERY_PLUGGED_USB = 2;
/** Power source is wireless. */
public static final int BATTERY_PLUGGED_WIRELESS = 4;
+
+ /** @hide */
+ public static final int BATTERY_PLUGGED_ANY =
+ BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a8cf21..3bbdf36 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -911,17 +911,20 @@
}
private static final HashSet<String> MOVED_TO_GLOBAL;
+ private static final HashSet<String> MOVED_TO_SECURE_THEN_GLOBAL;
static {
MOVED_TO_GLOBAL = new HashSet<String>();
+ MOVED_TO_SECURE_THEN_GLOBAL = new HashSet<String>();
+
// these were originally in system but migrated to secure in the past,
// so are duplicated in the Secure.* namespace
- MOVED_TO_GLOBAL.add(Global.ADB_ENABLED);
- MOVED_TO_GLOBAL.add(Global.BLUETOOTH_ON);
- MOVED_TO_GLOBAL.add(Global.DATA_ROAMING);
- MOVED_TO_GLOBAL.add(Global.DEVICE_PROVISIONED);
- MOVED_TO_GLOBAL.add(Global.INSTALL_NON_MARKET_APPS);
- MOVED_TO_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
- MOVED_TO_GLOBAL.add(Global.HTTP_PROXY);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.ADB_ENABLED);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.BLUETOOTH_ON);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DATA_ROAMING);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DEVICE_PROVISIONED);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.INSTALL_NON_MARKET_APPS);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.HTTP_PROXY);
// these are moving directly from system to global
MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
@@ -954,6 +957,17 @@
MOVED_TO_GLOBAL.add(Settings.Global.ALWAYS_FINISH_ACTIVITIES);
}
+ /** @hide */
+ public static void getMovedKeys(HashSet<String> outKeySet) {
+ outKeySet.addAll(MOVED_TO_GLOBAL);
+ outKeySet.addAll(MOVED_TO_SECURE_THEN_GLOBAL);
+ }
+
+ /** @hide */
+ public static void getNonLegacyMovedKeys(HashSet<String> outKeySet) {
+ outKeySet.addAll(MOVED_TO_GLOBAL);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -972,7 +986,7 @@
+ " to android.provider.Settings.Secure, returning read-only value.");
return Secure.getStringForUser(resolver, name, userHandle);
}
- if (MOVED_TO_GLOBAL.contains(name)) {
+ if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Global, returning read-only value.");
return Global.getStringForUser(resolver, name, userHandle);
@@ -999,7 +1013,7 @@
+ " to android.provider.Settings.Secure, value is unchanged.");
return false;
}
- if (MOVED_TO_GLOBAL.contains(name)) {
+ if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Global, value is unchanged.");
return false;
@@ -1019,7 +1033,7 @@
+ " to android.provider.Settings.Secure, returning Secure URI.");
return Secure.getUriFor(Secure.CONTENT_URI, name);
}
- if (MOVED_TO_GLOBAL.contains(name)) {
+ if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Global, returning read-only global URI.");
return Global.getUriFor(Global.CONTENT_URI, name);
@@ -2257,7 +2271,7 @@
* @hide
*/
public static final String[] SETTINGS_TO_BACKUP = {
- STAY_ON_WHILE_PLUGGED_IN,
+ STAY_ON_WHILE_PLUGGED_IN, // moved to global
WIFI_USE_STATIC_IP,
WIFI_STATIC_IP,
WIFI_STATIC_GATEWAY,
@@ -2272,7 +2286,7 @@
SCREEN_BRIGHTNESS_MODE,
SCREEN_AUTO_BRIGHTNESS_ADJ,
VIBRATE_INPUT_DEVICES,
- MODE_RINGER,
+ MODE_RINGER, // moved to global
MODE_RINGER_STREAMS_AFFECTED,
MUTE_STREAMS_AFFECTED,
VOLUME_VOICE,
@@ -2293,20 +2307,18 @@
TEXT_AUTO_CAPS,
TEXT_AUTO_PUNCTUATE,
TEXT_SHOW_PASSWORD,
- AUTO_TIME,
- AUTO_TIME_ZONE,
+ AUTO_TIME, // moved to global
+ AUTO_TIME_ZONE, // moved to global
TIME_12_24,
DATE_FORMAT,
DTMF_TONE_WHEN_DIALING,
DTMF_TONE_TYPE_WHEN_DIALING,
- Global.EMERGENCY_TONE,
- Global.CALL_AUTO_RETRY,
HEARING_AID,
TTY_MODE,
SOUND_EFFECTS_ENABLED,
HAPTIC_FEEDBACK_ENABLED,
- POWER_SOUNDS_ENABLED,
- DOCK_SOUNDS_ENABLED,
+ POWER_SOUNDS_ENABLED, // moved to global
+ DOCK_SOUNDS_ENABLED, // moved to global
LOCKSCREEN_SOUNDS_ENABLED,
SHOW_WEB_SUGGESTIONS,
NOTIFICATION_LIGHT_PULSE,
@@ -2702,6 +2714,11 @@
MOVED_TO_GLOBAL.add(Settings.Global.PREFERRED_CDMA_SUBSCRIPTION);
}
+ /** @hide */
+ public static void getMovedKeys(HashSet<String> outKeySet) {
+ outKeySet.addAll(MOVED_TO_GLOBAL);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -3993,12 +4010,11 @@
* @hide
*/
public static final String[] SETTINGS_TO_BACKUP = {
- ADB_ENABLED,
BUGREPORT_IN_POWER_MENU,
ALLOW_MOCK_LOCATION,
PARENTAL_CONTROL_ENABLED,
PARENTAL_CONTROL_REDIRECT_URL,
- USB_MASS_STORAGE_ENABLED,
+ USB_MASS_STORAGE_ENABLED, // moved to global
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
@@ -4017,9 +4033,9 @@
TTS_DEFAULT_COUNTRY,
TTS_ENABLED_PLUGINS,
TTS_DEFAULT_LOCALE,
- WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
- WIFI_NUM_OPEN_NETWORKS_KEPT,
+ WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, // moved to global
+ WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, // moved to global
+ WIFI_NUM_OPEN_NETWORKS_KEPT, // moved to global
MOUNT_PLAY_NOTIFICATION_SND,
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
@@ -5251,6 +5267,38 @@
public static final String ALWAYS_FINISH_ACTIVITIES =
"always_finish_activities";
+ /**
+ * Settings to backup. This is here so that it's in the same place as the settings
+ * keys and easy to update.
+ *
+ * These keys may be mentioned in the SETTINGS_TO_BACKUP arrays in System
+ * and Secure as well. This is because those tables drive both backup and
+ * restore, and restore needs to properly whitelist keys that used to live
+ * in those namespaces. The keys will only actually be backed up / restored
+ * if they are also mentioned in this table (Global.SETTINGS_TO_BACKUP).
+ *
+ * NOTE: Settings are backed up and restored in the order they appear
+ * in this array. If you have one setting depending on another,
+ * make sure that they are ordered appropriately.
+ *
+ * @hide
+ */
+ public static final String[] SETTINGS_TO_BACKUP = {
+ STAY_ON_WHILE_PLUGGED_IN,
+ MODE_RINGER,
+ AUTO_TIME,
+ AUTO_TIME_ZONE,
+ POWER_SOUNDS_ENABLED,
+ DOCK_SOUNDS_ENABLED,
+ USB_MASS_STORAGE_ENABLED,
+ ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
+ WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+ WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+ WIFI_NUM_OPEN_NETWORKS_KEPT,
+ EMERGENCY_TONE,
+ CALL_AUTO_RETRY,
+ };
+
// Populated lazily, guarded by class object:
private static NameValueCache sNameValueCache = new NameValueCache(
SYS_PROP_SETTING_VERSION,
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index cf58458..662dc45 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -79,38 +79,23 @@
public static final int DEFAULT_DISPLAY = 0;
/**
- * Display flag: Indicates that the display supports secure video output.
+ * Display flag: Indicates that the display supports compositing content
+ * that is stored in protected graphics buffers.
* <p>
- * This flag is used to indicate that the display supports content protection
- * mechanisms for secure video output at the display interface, such as HDCP.
- * These mechanisms may be used to protect secure content as it leaves the device.
+ * Secure (DRM) video decoders may allocate protected graphics buffers to request that
+ * a hardware-protected path be provided between the video decoder and the external
+ * display sink. If a hardware-protected path is not available, then content stored
+ * in protected graphics buffers may not be composited.
* </p><p>
- * While mirroring content to multiple displays, it can happen that certain
- * display devices support secure video output while other display devices do not.
- * The secure content will be shown only on the display devices that support
- * secure video output and will be blanked on other display devices that do
- * not support secure video output.
- * </p><p>
- * This flag mainly applies to external display devices such as HDMI or
- * Wifi display. Built-in display devices are usually considered secure.
+ * If this flag is not set, then the display device does not support compositing
+ * protected buffers; the user may see a blank region on the screen instead of
+ * the protected content. An application can use this flag as a hint that it should
+ * select an alternate content stream or adopt a different strategy for decoding
+ * content that does not rely on protected buffers so as to ensure that the user
+ * can view the content on the display as expected.
* </p>
- *
- * @hide pending review
*/
- public static final int FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT = 1 << 0;
-
- /**
- * Display flag: Indicates that the display supports secure in-memory video buffers.
- * <p>
- * This flag is used to indicate that the display supports content protection
- * mechanisms for in-memory video buffers, such as secure memory areas.
- * These mechanisms may be used to protect secure video buffers in memory from
- * the video decoder to the display compositor and the video interface.
- * </p>
- *
- * @hide pending review
- */
- public static final int FLAG_SUPPORTS_SECURE_VIDEO_BUFFERS = 1 << 1;
+ public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 0;
/**
* Internal method to create a display.
@@ -196,7 +181,7 @@
*
* @return The display flags.
*
- * @hide pending review
+ * @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
*/
public int getFlags() {
synchronized (this) {
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index c968ec5..fe05634 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -299,11 +299,8 @@
private static String flagsToString(int flags) {
StringBuilder result = new StringBuilder();
- if ((flags & Display.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT) != 0) {
- result.append(", FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT");
- }
- if ((flags & Display.FLAG_SUPPORTS_SECURE_VIDEO_BUFFERS) != 0) {
- result.append(", FLAG_SUPPORTS_SECURE_VIDEO_BUFFERS");
+ if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
+ result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
return result.toString();
}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 4873860..a74e438 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -19,9 +19,6 @@
import android.content.Context;
import android.os.SystemClock;
import android.util.FloatMath;
-import android.util.Log;
-
-import java.util.Arrays;
/**
* Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
@@ -143,11 +140,16 @@
private int mSpanSlop;
private int mMinSpan;
- private float[] mTouchHistoryLastAccepted;
- private int[] mTouchHistoryDirection;
- private long[] mTouchHistoryLastAcceptedTime;
+ // Bounds for recently seen values
+ private float mTouchUpper;
+ private float mTouchLower;
+ private float mTouchHistoryLastAccepted;
+ private int mTouchHistoryDirection;
+ private long mTouchHistoryLastAcceptedTime;
+ private int mTouchMinMajor;
private static final long TOUCH_STABILIZE_TIME = 128; // ms
+ private static final int TOUCH_MIN_MAJOR = 48; // dp
/**
* Consistency verifier for debugging purposes.
@@ -160,6 +162,8 @@
mContext = context;
mListener = listener;
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
+ mTouchMinMajor =
+ (int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f);
mMinSpan = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingSpan);
}
@@ -172,81 +176,55 @@
private void addTouchHistory(MotionEvent ev) {
final long currentTime = SystemClock.uptimeMillis();
final int count = ev.getPointerCount();
+ boolean accept = currentTime - mTouchHistoryLastAcceptedTime >= TOUCH_STABILIZE_TIME;
+ float total = 0;
+ int sampleCount = 0;
for (int i = 0; i < count; i++) {
- final int id = ev.getPointerId(i);
- ensureTouchHistorySize(id);
-
- final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted[id]);
- boolean accept = true;
+ final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted);
final int historySize = ev.getHistorySize();
- for (int h = 0; h < historySize + 1; h++) {
- final float major;
- final float minor;
+ final int pointerSampleCount = historySize + 1;
+ for (int h = 0; h < pointerSampleCount; h++) {
+ float major;
if (h < historySize) {
major = ev.getHistoricalTouchMajor(i, h);
- minor = ev.getHistoricalTouchMinor(i, h);
} else {
major = ev.getTouchMajor(i);
- minor = ev.getTouchMinor(i);
}
- final float avg = (major + minor) / 2;
+ if (major < mTouchMinMajor) major = mTouchMinMajor;
+ total += major;
+
+ if (Float.isNaN(mTouchUpper) || major > mTouchUpper) {
+ mTouchUpper = major;
+ }
+ if (Float.isNaN(mTouchLower) || major < mTouchLower) {
+ mTouchLower = major;
+ }
if (hasLastAccepted) {
- final int directionSig = (int) Math.signum(avg - mTouchHistoryLastAccepted[id]);
- if (directionSig != mTouchHistoryDirection[id] ||
- (directionSig == 0 && mTouchHistoryDirection[id] == 0)) {
- mTouchHistoryDirection[id] = directionSig;
+ final int directionSig = (int) Math.signum(major - mTouchHistoryLastAccepted);
+ if (directionSig != mTouchHistoryDirection ||
+ (directionSig == 0 && mTouchHistoryDirection == 0)) {
+ mTouchHistoryDirection = directionSig;
final long time = h < historySize ? ev.getHistoricalEventTime(h)
: ev.getEventTime();
- mTouchHistoryLastAcceptedTime[id] = time;
- accept = false;
- }
- if (currentTime - mTouchHistoryLastAcceptedTime[id] < TOUCH_STABILIZE_TIME) {
+ mTouchHistoryLastAcceptedTime = time;
accept = false;
}
}
}
-
- if (accept) {
- float newAccepted = (ev.getTouchMajor(i) + ev.getTouchMinor(i)) / 2;
- if (hasLastAccepted) {
- newAccepted = (mTouchHistoryLastAccepted[id] + newAccepted) / 2;
- }
- mTouchHistoryLastAccepted[id] = newAccepted;
- mTouchHistoryDirection[id] = 0;
- mTouchHistoryLastAcceptedTime[id] = ev.getEventTime();
- }
+ sampleCount += pointerSampleCount;
}
- }
- /**
- * Clear out the touch history for a given pointer id.
- * @param id pointer id to clear
- * @see #addTouchHistory(MotionEvent)
- */
- private boolean removeTouchHistoryForId(int id) {
- if (id >= mTouchHistoryLastAccepted.length) {
- return false;
- }
- mTouchHistoryLastAccepted[id] = Float.NaN;
- mTouchHistoryDirection[id] = 0;
- mTouchHistoryLastAcceptedTime[id] = 0;
- return true;
- }
+ final float avg = total / sampleCount;
- /**
- * Get the adjusted combined touchMajor/touchMinor value for a given pointer id
- * @param id the pointer id of the data to obtain
- * @return the adjusted major/minor value for the point at id
- * @see #addTouchHistory(MotionEvent)
- */
- private float getAdjustedTouchHistory(int id) {
- if (id >= mTouchHistoryLastAccepted.length) {
- Log.e(TAG, "Error retrieving adjusted touch history for id=" + id +
- " - incomplete event stream?");
- return 0;
+ if (accept) {
+ float newAccepted = (mTouchUpper + mTouchLower + avg) / 3;
+ mTouchUpper = (mTouchUpper + newAccepted) / 2;
+ mTouchLower = (mTouchLower + newAccepted) / 2;
+ mTouchHistoryLastAccepted = newAccepted;
+ mTouchHistoryDirection = 0;
+ mTouchHistoryLastAcceptedTime = ev.getEventTime();
}
- return mTouchHistoryLastAccepted[id];
}
/**
@@ -254,41 +232,11 @@
* @see #addTouchHistory(MotionEvent)
*/
private void clearTouchHistory() {
- if (mTouchHistoryLastAccepted == null) {
- // All three arrays will be null if this is the case; nothing to do.
- return;
- }
- Arrays.fill(mTouchHistoryLastAccepted, Float.NaN);
- Arrays.fill(mTouchHistoryDirection, 0);
- Arrays.fill(mTouchHistoryLastAcceptedTime, 0);
- }
-
- private void ensureTouchHistorySize(int id) {
- final int requiredSize = id + 1;
- if (mTouchHistoryLastAccepted == null || mTouchHistoryLastAccepted.length < requiredSize) {
- final float[] newLastAccepted = new float[requiredSize];
- final int[] newDirection = new int[requiredSize];
- final long[] newLastAcceptedTime = new long[requiredSize];
-
- int oldLength = 0;
- if (mTouchHistoryLastAccepted != null) {
- System.arraycopy(mTouchHistoryLastAccepted, 0, newLastAccepted, 0,
- mTouchHistoryLastAccepted.length);
- System.arraycopy(mTouchHistoryDirection, 0, newDirection, 0,
- mTouchHistoryDirection.length);
- System.arraycopy(mTouchHistoryLastAcceptedTime, 0, newLastAcceptedTime, 0,
- mTouchHistoryLastAcceptedTime.length);
- oldLength = mTouchHistoryLastAccepted.length;
- }
-
- Arrays.fill(newLastAccepted, oldLength, newLastAccepted.length, Float.NaN);
- Arrays.fill(newDirection, oldLength, newDirection.length, 0);
- Arrays.fill(newLastAcceptedTime, oldLength, newLastAcceptedTime.length, 0);
-
- mTouchHistoryLastAccepted = newLastAccepted;
- mTouchHistoryDirection = newDirection;
- mTouchHistoryLastAcceptedTime = newLastAcceptedTime;
- }
+ mTouchUpper = Float.NaN;
+ mTouchLower = Float.NaN;
+ mTouchHistoryLastAccepted = Float.NaN;
+ mTouchHistoryDirection = 0;
+ mTouchHistoryLastAcceptedTime = 0;
}
/**
@@ -346,23 +294,16 @@
final float focusX = sumX / div;
final float focusY = sumY / div;
- if (pointerUp) {
- final int id = event.getPointerId(event.getActionIndex());
- if (!removeTouchHistoryForId(id)) {
- Log.e(TAG, "Got ACTION_POINTER_UP for previously unknown id=" + id +
- " - incomplete event stream?");
- }
- } else {
- addTouchHistory(event);
- }
+
+ addTouchHistory(event);
// Determine average deviation from focal point
float devSumX = 0, devSumY = 0;
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
- // Average touch major and touch minor and convert the resulting diameter into a radius.
- final float touchSize = getAdjustedTouchHistory(event.getPointerId(i)) / 2;
+ // Convert the resulting diameter into a radius.
+ final float touchSize = mTouchHistoryLastAccepted / 2;
devSumX += Math.abs(event.getX(i) - focusX) + touchSize;
devSumY += Math.abs(event.getY(i) - focusY) + touchSize;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3ed47ea..2d083b6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5871,6 +5871,8 @@
((layoutDirection << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) & PFLAG2_LAYOUT_DIRECTION_MASK);
// We need to resolve all RTL properties as they all depend on layout direction
resolveRtlPropertiesIfNeeded();
+ requestLayout();
+ invalidate(true);
}
}
@@ -11568,8 +11570,6 @@
if (!isDrawablesResolved()) {
resolveDrawables();
}
- requestLayout();
- invalidate(true);
onRtlPropertiesChanged(getLayoutDirection());
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7f0af09..871f752 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1055,30 +1055,30 @@
if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
(mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
- boolean newValue = !mCheckStates.get(position, false);
- mCheckStates.put(position, newValue);
+ boolean checked = !mCheckStates.get(position, false);
+ mCheckStates.put(position, checked);
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- if (newValue) {
+ if (checked) {
mCheckedIdStates.put(mAdapter.getItemId(position), position);
} else {
mCheckedIdStates.delete(mAdapter.getItemId(position));
}
}
- if (newValue) {
+ if (checked) {
mCheckedItemCount++;
} else {
mCheckedItemCount--;
}
if (mChoiceActionMode != null) {
mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
- position, id, newValue);
+ position, id, checked);
dispatchItemClick = false;
}
checkedStateChanged = true;
} else if (mChoiceMode == CHOICE_MODE_SINGLE) {
- boolean newValue = !mCheckStates.get(position, false);
- if (newValue) {
- mCheckStates.clear();
+ boolean checked = !mCheckStates.get(position, false);
+ mCheckStates.clear();
+ if (checked) {
mCheckStates.put(position, true);
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
mCheckedIdStates.clear();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c67cae6..19b825c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -122,6 +122,7 @@
InputMethodState mInputMethodState;
DisplayList[] mTextDisplayLists;
+ int mLastLayoutHeight;
boolean mFrozenWithFocus;
boolean mSelectionMoved;
@@ -1258,6 +1259,16 @@
mTextDisplayLists = new DisplayList[ArrayUtils.idealObjectArraySize(0)];
}
+ // If the height of the layout changes (usually when inserting or deleting a line,
+ // but could be changes within a span), invalidate everything. We could optimize
+ // more aggressively (for example, adding offsets to blocks) but it would be more
+ // complex and we would only get the benefit in some cases.
+ int layoutHeight = layout.getHeight();
+ if (mLastLayoutHeight != layoutHeight) {
+ invalidateTextDisplayList();
+ mLastLayoutHeight = layoutHeight;
+ }
+
DynamicLayout dynamicLayout = (DynamicLayout) layout;
int[] blockEndLines = dynamicLayout.getBlockEndLines();
int[] blockIndices = dynamicLayout.getBlockIndices();
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 317baf1..925864c 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -30,8 +30,11 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.PopupWindow.OnDismissListener;
/**
@@ -978,6 +981,30 @@
super.show();
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
setSelection(Spinner.this.getSelectedItemPosition());
+
+ // Make sure we hide if our anchor goes away.
+ // TODO: This might be appropriate to push all the way down to PopupWindow,
+ // but it may have other side effects to investigate first. (Text editing handles, etc.)
+ final ViewTreeObserver vto = getViewTreeObserver();
+ if (vto != null) {
+ final OnGlobalLayoutListener layoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (!Spinner.this.isVisibleToUser()) {
+ dismiss();
+ }
+ }
+ };
+ vto.addOnGlobalLayoutListener(layoutListener);
+ setOnDismissListener(new OnDismissListener() {
+ @Override public void onDismiss() {
+ final ViewTreeObserver vto = getViewTreeObserver();
+ if (vto != null) {
+ vto.removeOnGlobalLayoutListener(layoutListener);
+ }
+ }
+ });
+ }
}
}
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 0d9cf9a..6c5ed7e 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -375,31 +375,31 @@
}
private void notifyCellAdded() {
+ sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
if (mOnPatternListener != null) {
mOnPatternListener.onPatternCellAdded(mPattern);
}
- sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
}
private void notifyPatternStarted() {
+ sendAccessEvent(R.string.lockscreen_access_pattern_start);
if (mOnPatternListener != null) {
mOnPatternListener.onPatternStart();
}
- sendAccessEvent(R.string.lockscreen_access_pattern_start);
}
private void notifyPatternDetected() {
+ sendAccessEvent(R.string.lockscreen_access_pattern_detected);
if (mOnPatternListener != null) {
mOnPatternListener.onPatternDetected(mPattern);
}
- sendAccessEvent(R.string.lockscreen_access_pattern_detected);
}
private void notifyPatternCleared() {
+ sendAccessEvent(R.string.lockscreen_access_pattern_cleared);
if (mOnPatternListener != null) {
mOnPatternListener.onPatternCleared();
}
- sendAccessEvent(R.string.lockscreen_access_pattern_cleared);
}
/**
@@ -799,9 +799,7 @@
}
private void sendAccessEvent(int resId) {
- setContentDescription(mContext.getString(resId));
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- setContentDescription(null);
+ announceForAccessibility(mContext.getString(resId));
}
private void handleActionUp(MotionEvent event) {
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 2a02f7c..5d6f738 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -20,6 +20,7 @@
#include "SkCanvas.h"
#include "SkDevice.h"
+#include "SkDrawFilter.h"
#include "SkGraphics.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPorterDuff.h"
@@ -784,7 +785,15 @@
#define kStdUnderline_Thickness (1.0f / 18.0f)
static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) {
- uint32_t flags = paint->getFlags();
+ uint32_t flags;
+ SkDrawFilter* drawFilter = canvas->getDrawFilter();
+ if (drawFilter) {
+ SkPaint paintCopy(*paint);
+ drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ flags = paintCopy.getFlags();
+ } else {
+ flags = paint->getFlags();
+ }
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
SkScalar left = SkFloatToScalar(x);
SkScalar right = SkFloatToScalar(x + length);
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
deleted file mode 100644
index c30eb1c..0000000
--- a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
deleted file mode 100644
index e5d5771..0000000
--- a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
index df3ee00..d6a858f 100644
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -22,6 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="125dp"
android:layout_height="125dp"
+ android:background="#000000"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/keyguard_user_avatar"
@@ -29,12 +30,23 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
- <TextView
- android:id="@+id/keyguard_user_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
- android:textSize="12sp"
- android:background="#99FFFFFF"
- android:textColor="#ff000000"/>
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.78" />
+ <TextView
+ android:id="@+id/keyguard_user_name"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.22"
+ android:paddingLeft="6dp"
+ android:layout_gravity="center_vertical|left"
+ android:textSize="16sp"
+ android:textColor="#ffffff"
+ android:background="#808080" />
+ </LinearLayout>
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
index c599fd5dd..5a6e998 100644
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -21,7 +21,8 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center">
+ android:layout_gravity="center"
+ android:contentDescription="@string/keyguard_accessibility_user_selector">
<com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
android:id="@+id/keyguard_users_grid"
diff --git a/core/res/res/layout/keyguard_multi_user_selector_widget.xml b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
index c00089c..ad9fdfe 100644
--- a/core/res/res/layout/keyguard_multi_user_selector_widget.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
@@ -20,6 +20,7 @@
<!-- This is a view that shows general status information in Keyguard. -->
<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_multi_user_selector"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index dc596f9..c7f6863 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -29,7 +29,8 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_horizontal">
+ android:gravity="center_horizontal"
+ android:contentDescription="@string/keyguard_accessibility_status">
<com.android.internal.policy.impl.keyguard.ClockView
android:id="@+id/clock_view"
diff --git a/core/res/res/layout/keyguard_transport_control_view.xml b/core/res/res/layout/keyguard_transport_control_view.xml
index c40aa66..5a6083a 100644
--- a/core/res/res/layout/keyguard_transport_control_view.xml
+++ b/core/res/res/layout/keyguard_transport_control_view.xml
@@ -26,7 +26,8 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:foreground="@drawable/ic_lockscreen_player_background">
+ android:foreground="@drawable/ic_lockscreen_player_background"
+ android:contentDescription="@string/keygaurd_accessibility_media_controls">
<!-- Use ImageView for its cropping features; otherwise could be android:background -->
<ImageView
android:id="@+id/albumart"
diff --git a/core/res/res/layout/keyguard_widget_region.xml b/core/res/res/layout/keyguard_widget_region.xml
index 42bf42b..01b5551 100644
--- a/core/res/res/layout/keyguard_widget_region.xml
+++ b/core/res/res/layout/keyguard_widget_region.xml
@@ -32,16 +32,14 @@
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false">
- <!-- TODO: Remove this when supported as a widget -->
- <include layout="@layout/keyguard_status_view"/>
- <include layout="@layout/keyguard_transport_control_view"/>
</com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="10dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/kg_widget_pager_horizontal_padding"
- android:paddingRight="@dimen/kg_widget_pager_horizontal_padding">
+ android:paddingRight="@dimen/kg_widget_pager_horizontal_padding"
+ android:layout_marginTop="@dimen/kg_runway_lights_top_margin">
<com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
android:id="@+id/left_strip"
android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
@@ -69,4 +67,4 @@
prvandroid:leftToRight="true"
prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
</LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion>
\ No newline at end of file
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 4e202ac..0c36d4a 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -77,5 +77,13 @@
<!-- Preference fragment padding, sides -->
<dimen name="preference_fragment_padding_side">24dp</dimen>
<dimen name="preference_screen_header_padding_side">24dip</dimen>
+
+ <!-- Keyguard dimensions -->
+ <!-- Bottom padding for the widget pager -->
+ <dimen name="kg_widget_pager_bottom_padding">16dp</dimen>
+
+ <!-- Top margin for the runway lights. We add a negative margin in large
+ devices to account for the widget pager padding -->
+ <dimen name="kg_runway_lights_top_margin">-10dp</dimen>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 07d0d74..6a93f30 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -115,6 +115,11 @@
<color name="lockscreen_clock_am_pm">#ffffffff</color>
<color name="lockscreen_owner_info">#ff9a9a9a</color>
+ <!-- keyguard overscroll widget pager -->
+ <color name="kg_multi_user_text_active">#ffffffff</color>
+ <color name="kg_multi_user_text_inactive">#ff808080</color>
+ <color name="kg_widget_pager_gradient">#ff33B5E5</color>
+
<!-- FaceLock -->
<color name="facelock_spotlight_mask">#CC000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d3d994f..16960c8 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -967,4 +967,18 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">true</bool>
+ <!-- Set to true if the wifi display supports compositing content stored
+ in gralloc protected buffers. For this to be true, there must exist
+ a protected hardware path for surface flinger to composite and send
+ protected buffers to the wifi display video encoder.
+
+ If this flag is false, we advise applications not to use protected
+ buffers (if possible) when presenting content to a wifi display because
+ the content may be blanked.
+
+ This flag controls whether the {@link Display#FLAG_SUPPORTS_PROTECTED_BUFFERS}
+ flag is set for wifi displays.
+ -->
+ <bool name="config_wifiDisplaySupportsProtectedBuffers">false</bool>
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 289adf4..10f0d39 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -286,7 +286,7 @@
<dimen name="kg_runway_lights_height">7dp</dimen>
<!-- The height of the runway lights strip -->
- <dimen name="kg_runway_lights_vertical_padding">3dp</dimen>
+ <dimen name="kg_runway_lights_vertical_padding">2dp</dimen>
<!-- Horizontal padding for the widget pager -->
<dimen name="kg_widget_pager_horizontal_padding">16dp</dimen>
@@ -297,6 +297,10 @@
<!-- Bottom padding for the widget pager -->
<dimen name="kg_widget_pager_bottom_padding">6dp</dimen>
+ <!-- Top margin for the runway lights. We add a negative margin in large
+ devices to account for the widget pager padding -->
+ <dimen name="kg_runway_lights_top_margin">0dp</dimen>
+
<!-- Touch slop for the global toggle accessibility gesture -->
<dimen name="accessibility_touch_slop">80dip</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2f9ce0c..fb8005a 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2272,6 +2272,15 @@
<!-- Accessibility description sent when user completes drawing a pattern. [CHAR LIMIT=NONE] -->
<string name="lockscreen_access_pattern_detected">Pattern completed</string>
+ <!-- Accessibility description sent when user changes the current lock screen widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_changed">%1$s. Widget %2$d of %3$d.</string>
+ <!-- Accessibility description of the lock screen user selector widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_user_selector">User selector</string>
+ <!-- Accessibility description of the lock screen status widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_status">Status</string>
+ <!-- Accessibility description of the lock media control widget. [CHAR_LIMIT=none] -->
+ <string name="keygaurd_accessibility_media_controls">Media controls</string>
+
<!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="password_keyboard_label_symbol_key">\?123</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cda13bd..d423ecd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -261,6 +261,7 @@
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
<java-symbol type="bool" name="config_voice_capable" />
+ <java-symbol type="bool" name="config_wifiDisplaySupportsProtectedBuffers" />
<java-symbol type="bool" name="preferences_prefer_dual_pane" />
<java-symbol type="bool" name="skip_restoring_network_selection" />
<java-symbol type="bool" name="split_action_bar_is_narrow" />
@@ -548,6 +549,10 @@
<java-symbol type="string" name="keyboardview_keycode_enter" />
<java-symbol type="string" name="keyboardview_keycode_mode_change" />
<java-symbol type="string" name="keyboardview_keycode_shift" />
+ <java-symbol type="string" name="keygaurd_accessibility_media_controls" />
+ <java-symbol type="string" name="keyguard_accessibility_status" />
+ <java-symbol type="string" name="keyguard_accessibility_user_selector" />
+ <java-symbol type="string" name="keyguard_accessibility_widget_changed" />
<java-symbol type="string" name="kilobyteShort" />
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
@@ -1098,6 +1103,7 @@
<java-symbol type="xml" name="kg_password_kbd_numeric" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="time_zones_by_country" />
+ <java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="raw" name="accessibility_gestures" />
<java-symbol type="raw" name="incognito_mode_start_page" />
@@ -1182,6 +1188,9 @@
<java-symbol type="bool" name="config_reverseDefaultRotation" />
<java-symbol type="bool" name="config_showNavigationBar" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
+ <java-symbol type="color" name="kg_multi_user_text_active" />
+ <java-symbol type="color" name="kg_multi_user_text_inactive" />
+ <java-symbol type="color" name="kg_widget_pager_gradient" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
@@ -1207,8 +1216,6 @@
<java-symbol type="drawable" name="magnified_region_frame" />
<java-symbol type="drawable" name="menu_background" />
<java-symbol type="drawable" name="stat_sys_secure" />
- <java-symbol type="drawable" name="kg_widget_overscroll_layer_left" />
- <java-symbol type="drawable" name="kg_widget_overscroll_layer_right" />
<java-symbol type="id" name="action_mode_bar_stub" />
<java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="backspace" />
@@ -1292,6 +1299,7 @@
<java-symbol type="id" name="kg_widget_region" />
<java-symbol type="id" name="left_strip" />
<java-symbol type="id" name="right_strip" />
+ <java-symbol type="id" name="keyguard_multi_user_selector" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
@@ -1331,6 +1339,8 @@
<java-symbol type="layout" name="screen_title" />
<java-symbol type="layout" name="screen_title_icons" />
<java-symbol type="layout" name="keyguard_host_view" />
+ <java-symbol type="layout" name="keyguard_transport_control_view" />
+ <java-symbol type="layout" name="keyguard_status_view" />
<java-symbol type="string" name="abbrev_wday_month_day_no_year" />
<java-symbol type="string" name="android_upgrading_title" />
<java-symbol type="string" name="bugreport_title" />
@@ -1400,7 +1410,6 @@
<java-symbol type="string" name="kg_password_wrong_pin_code" />
<java-symbol type="string" name="kg_invalid_sim_pin_hint" />
<java-symbol type="string" name="kg_invalid_sim_puk_hint" />
- <java-symbol type="string" name="kg_sim_puk_recovery_hint" />
<java-symbol type="string" name="kg_invalid_puk" />
<java-symbol type="string" name="kg_login_too_many_attempts" />
<java-symbol type="string" name="kg_login_instructions" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
new file mode 100644
index 0000000..8b395af
--- /dev/null
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<!-- Regex patterns for SMS short codes by country. -->
+<shortcodes>
+
+ <!-- The country attribute is the ISO country code of the user's account (from SIM card or NV).
+ The pattern attribute is a regex that matches all SMS short codes for the country.
+ The premium attribute is a regex that matches premium rate SMS short codes.
+ The free attribute matches short codes that we know will not cost the user, such as
+ emergency numbers. The standard attribute matches short codes that are billed at the
+ standard SMS rate. The user is warned when the destination phone number matches the
+ "pattern" or "premium" regexes, and does not match the "free" or "standard" regexes. -->
+
+ <!-- Harmonised European Short Codes are 6 digit numbers starting with 116 (free helplines).
+ Premium patterns include short codes from: http://aonebill.com/coverage&tariffs
+ and http://mobilcent.com/info-worldwide.asp and extracted from:
+ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
+
+ <!-- Albania: 5 digits, known short codes listed -->
+ <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
+
+ <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
+ <shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
+
+ <!-- Austria: 10 digits, premium prefix 09xx, plus EU -->
+ <shortcode country="at" pattern="11\\d{4}" premium="09.*" free="116\\d{3}" />
+
+ <!-- Australia: 6 or 8 digits starting with "19" -->
+ <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882" />
+
+ <!-- Azerbaijan: 4-5 digits, known premium codes listed -->
+ <shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+
+ <!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
+ <shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
+
+ <!-- Bulgaria: 4-5 digits, plus EU -->
+ <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}" />
+
+ <!-- Belarus: 4 digits -->
+ <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
+
+ <!-- Canada: 5-6 digits -->
+ <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188" />
+
+ <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
+ <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111" />
+
+ <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
+ http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
+ <shortcode country="cn" premium="1066.*" free="1065.*" />
+
+ <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
+
+ <!-- Czech Republic: 7-8 digits, starting with 9, plus EU:
+ http://www.o2.cz/osobni/en/services-by-alphabet/91670-premium_sms.html -->
+ <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
+
+ <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}" />
+
+ <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
+ <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
+
+ <!-- Estonia: short codes 3-5 digits starting with 1, plus premium 7 digit numbers starting with 90, plus EU.
+ http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
+ <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}" />
+
+ <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
+ http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
+ <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}" />
+
+ <!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
+ <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
+
+ <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
+ http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements -->
+ <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}" />
+
+ <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
+ http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf -->
+ <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}" />
+
+ <!-- Georgia: 4 digits, known premium codes listed -->
+ <shortcode country="ge" pattern="\\d{4}" premium="801[234]|888[239]" />
+
+ <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
+ <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}" />
+
+ <!-- Hungary: 4 or 10 digits starting with 1 or 0, plus EU:
+ http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
+ <shortcode country="hu" pattern="[01](?:\\d{3}|\\d{9})" premium="0691227910|1784" free="116\\d{3}" />
+
+ <!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
+ http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
+ <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
+
+ <!-- Israel: 4 digits, known premium codes listed -->
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
+
+ <!-- Italy: 5 digits (premium=4xxxx), plus EU:
+ http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
+ <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}" />
+
+ <!-- Kyrgyzstan: 4 digits, known premium codes listed -->
+ <shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
+
+ <!-- Kazakhstan: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-kazakhstan/ -->
+ <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
+
+ <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
+ <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}" />
+
+ <!-- Luxembourg: 5 digits, 6xxxx, plus EU:
+ http://www.luxgsm.lu/assets/files/filepage/file_1253803400.pdf -->
+ <shortcode country="lu" premium="6\\d{4}" free="116\\d{3}" />
+
+ <!-- Latvia: 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="lv" pattern="\\d{4}" premium="18(?:19|63|7[1-4])" free="116\\d{3}" />
+
+ <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" />
+
+ <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
+ <shortcode country="my" pattern="\\d{5}" premium="32298|33776" />
+
+ <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}" />
+
+ <!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" />
+
+ <!-- New Zealand: 3-4 digits, known premium codes listed -->
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995" />
+
+ <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}" />
+
+ <!-- Portugal: 5 digits, plus EU:
+ http://clients.txtnation.com/entries/158326-portugal-premium-sms-short-code-regulations -->
+ <shortcode country="pt" premium="6[1289]\\d{3}" free="116\\d{3}" />
+
+ <!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
+ <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}" />
+
+ <!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
+ <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)" />
+
+ <!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
+ <shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
+
+ <!-- Singapore: 5 digits: http://clients.txtnation.com/entries/306442-singapore-premium-sms-short-code-requirements
+ Free government directory info at 74688: http://app.sgdi.gov.sg/sms_help.asp -->
+ <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688" />
+
+ <!-- Slovenia: 4 digits (premium=3xxx, 6xxx, 8xxx), plus EU: http://www.cmtelecom.com/premium-sms/slovenia -->
+ <shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
+
+ <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
+ <shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
+
+ <!-- Tajikistan: 4 digits, known premium codes listed -->
+ <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+
+ <!-- Ukraine: 4 digits, known premium codes listed -->
+ <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
+
+ <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm) -->
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" />
+
+</shortcodes>
diff --git a/docs/html/distribute/distribute_toc.cs b/docs/html/distribute/distribute_toc.cs
index 84103b9..ad3121c 100644
--- a/docs/html/distribute/distribute_toc.cs
+++ b/docs/html/distribute/distribute_toc.cs
@@ -28,9 +28,7 @@
<li><a href="<?cs var:toroot ?>distribute/googleplay/publish/preparing.html">
<span class="en">Publishing Checklist</span>
</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">
- <span class="en">App Quality</span>
- </a></li>
+
</ul>
</li>
@@ -79,6 +77,26 @@
</ul>
</li>
+
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/quality/index.html">
+ <span class="en">App Quality</span></a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/core.html">
+ <span class="en">Core App Quality</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/tablet.html">
+ <span class="en">Tablet App Quality</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">
+ <span class="en">Improving App Quality</span>
+ </a></li>
+
+ </ul>
+ </li>
+
+
<!--
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/after.html">
@@ -92,17 +110,17 @@
</li>
-->
-<!--
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/strategies/index.html">
- <span class="en">Strategies</span></a>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/index.html">
+ <span class="en">Spotlight</span></a>
</div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/featuring.html">Featuring</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">App Quality</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/tablets.html">
+ <span class="en">Tablet Stories</span>
+ </a></li>
</ul>
</li>
--->
+
<li class="nav-section">
<div class="nav-section-header empty">
<a href="<?cs var:toroot ?>distribute/open.html">
diff --git a/docs/html/distribute/googleplay/about/monetizing.jd b/docs/html/distribute/googleplay/about/monetizing.jd
index 4980eda..d5c6dfa 100644
--- a/docs/html/distribute/googleplay/about/monetizing.jd
+++ b/docs/html/distribute/googleplay/about/monetizing.jd
@@ -42,7 +42,7 @@
<h3 id="payment-methods">Convenient payment options</h3>
<p>Users can purchase your products on Google Play using several convenient
-payment methods—credit cards, Direct Carrier Billing, and Google Play balance.</p>
+payment methods—credit cards, Direct Carrier Billing, gift cards, and Google Play balance.</p>
<p><span style="font-weight:500">Credit card</span> is the most common method of payment. Users can pay using any credit card
that they’ve registered in Google Play. To make it easy for users to get started,
@@ -52,8 +52,9 @@
<div class="sidebox">
<h2>Payment methods on Google Play</h2>
<ul>
-<li>Credit Card</li>
+<li>Credit card</li>
<li>Direct Carrier Billing</li>
+<li>Gift card</li>
<li>Google Play balance (stored value)</li>
</ul>
</div>
diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd
index 6ea0f53..463343d 100644
--- a/docs/html/distribute/googleplay/publish/preparing.jd
+++ b/docs/html/distribute/googleplay/publish/preparing.jd
@@ -6,20 +6,21 @@
<ol>
<li><a href="#process">1. Understand the publishing process</a></li>
<li><a href="#policies">2. Understand Google Play policies</a></li>
-<li><a href="#rating">3. Determine your content rating</a></li>
-<li><a href="#countries">4. Determine country distribution</a></li>
-<li><a href="#size">5. Confirm the app's overall size</a></li>
-<li><a href="#compatibility">6. Confirm app compatibility ranges</a></li>
-<li><a href="#free-priced">7. Decide on free or priced</a></li>
-<li><a href="#inapp-billing">8. Consider In-app Billing</a></li>
-<li><a href="#pricing">9. Set prices for your apps</a></li>
-<li><a href="#localize">10. Start localization</a></li>
-<li><a href="#localize">11. Prepare promotional graphics</a></li>
-<li><a href="#apk">12. Build the release-ready APK</a></li>
-<li><a href="#product-page">13. Complete the product details</a></li>
-<li><a href="#badges">14. Use Google Play badges and links to your promotional campaigns</a></li>
-<li><a href="#final-checks">15. Final checks and publishing</a></li>
-<li><a href="#support">16. Support users after launch</a></li>
+<li><a href="#core-app-quality">3. Test for Core App Quality</a></li>
+<li><a href="#rating">4. Determine your content rating</a></li>
+<li><a href="#countries">5. Determine country distribution</a></li>
+<li><a href="#size">6. Confirm the app's overall size</a></li>
+<li><a href="#compatibility">7. Confirm app compatibility ranges</a></li>
+<li><a href="#free-priced">8. Decide on free or priced</a></li>
+<li><a href="#inapp-billing">9. Consider In-app Billing</a></li>
+<li><a href="#pricing">10. Set prices for your apps</a></li>
+<li><a href="#localize">11. Start localization early</a></li>
+<li><a href="#localize">12. Prepare promotional graphics</a></li>
+<li><a href="#apk">13. Build the release-ready APK</a></li>
+<li><a href="#product-page">14. Complete the product details</a></li>
+<li><a href="#badges">15. Use Google Play badges</a></li>
+<li><a href="#final-checks">16. Final checks and publishing</a></li>
+<li><a href="#support">17. Support users after launch</a></li>
</ol>
</div></div>
@@ -86,7 +87,39 @@
</tr>
</table>
-<h2 id="rating">3. Determine your app's content rating</h2>
+<h2 id="core-app-quality">3. Test for Core App Quality</h2>
+
+<p>Before you publish an app on Google Play, it's important to make sure that
+it meets the basic quality expectations for all Android apps, on all of the devices that you
+are targeting. You can check your app's quality by setting up a test
+environment and testing the app against a short set of <strong>core app quality criteria</strong>.
+For complete information, see the <a
+href="{@docRoot}distribute/googleplay/quality/core.html">Core App Quality Guidelines</a>.
+</p>
+
+<p>If your app is targeting tablet devices, make sure that it delivers a rich, compelling
+experience to your tablet customers. See the <a
+href="{@docRoot}distribute/googleplay/quality/tablet.html">Tablet App Quality Checklist</a>
+for recommendations on ways to optimize your app for tablets.</p>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a
+href="{@docRoot}distribute/googleplay/quality/core.html">Core App Quality
+Guidelines</a></strong> — A set of core quality criteria that all Android
+apps should meet on all targeted devices.</li>
+<li><strong><a
+href="{@docRoot}distribute/googleplay/quality/tablet.html">Tablet App Quality
+Checklist</a></strong> — A set recommendations for delivering the best
+possible experience to tablet users.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+<h2 id="rating">4. Determine your app's content rating</h2>
<p>Google Play requires you to set a content rating for your app, which informs
Google Play users of its maturity level. Before you publish, you should confirm
@@ -115,7 +148,7 @@
</tr>
</table>
-<h2 id="countries">4. Determine country distribution</h2>
+<h2 id="countries">5. Determine country distribution</h2>
<p>Google Play lets you control what countries and territories your app is
distributed to. For widest reach and the largest potential customer base, you
@@ -149,7 +182,7 @@
</tr>
</table>
-<h2 id="size">5. Confirm the app's overall size</h2>
+<h2 id="size">6. Confirm the app's overall size</h2>
<p>The overall size of your app can affect its design and how you publish it on
Google Play. Currently, the maximum size for an APK published on Google Play is
@@ -180,7 +213,7 @@
</tr>
</table>
-<h2 id="compatibility">6. Confirm the app's platform and screen compatibility ranges</h2>
+<h2 id="compatibility">7. Confirm the app's platform and screen compatibility ranges</h2>
<p>Before publishing, it's important to make sure that your app is designed to
run properly on the Android platform versions and device screen sizes that you
@@ -217,7 +250,7 @@
</tr>
</table>
-<h2 id="free-priced">7. Decide whether your app will be free or priced</h2>
+<h2 id="free-priced">8. Decide whether your app will be free or priced</h2>
<p>On Google Play, you can publish apps as free to download or priced. Free apps
can be downloaded by any Android user in Google Play.
@@ -249,7 +282,7 @@
</tr>
</table>
-<h2 id="inapp-billing">8. Consider using In-app Billing</h2>
+<h2 id="inapp-billing">9. Consider using In-app Billing</h2>
<p>Google Play <a href="{@docRoot}guide/google/play/billing/index.html">In-app
Billing</a> lets you sell digital content in your applications. You can use the
@@ -275,7 +308,7 @@
</tr>
</table>
-<h2 id="pricing">9. Set prices for your products</h2>
+<h2 id="pricing">10. Set prices for your products</h2>
<p>If your app is priced or you will sell in-app products, Google Play lets you
set prices for your products in a variety of currencies, for users in markets
@@ -308,7 +341,7 @@
</tr>
</table>
-<h2 id="localize">10. Start localization</h2>
+<h2 id="localize">11. Start localization</h2>
<p>With your country targeting in mind, it's a good idea to assess your localization
needs and start the work of localizing well in advance of your target
@@ -344,7 +377,7 @@
</tr>
</table>
-<h2 id="graphics">11. Prepare promotional graphics</h2>
+<h2 id="graphics">12. Prepare promotional graphics</h2>
<p>When you publish on Google Play, you can supply a variety of high-quality
graphic assets to showcase your app or brand. After you publish, these appear on
@@ -375,7 +408,7 @@
</tr>
</table>
-<h2 id="apk">12. Build and upload the release-ready APK</h2>
+<h2 id="apk">13. Build and upload the release-ready APK</h2>
<p>When you are satisfied that your app meets your UI, compatibility, and
quality requirements, you can build the release-ready version of the app. The
@@ -407,7 +440,7 @@
</tr>
</table>
-<h2 id="product-page">13. Complete the app's product details</h2>
+<h2 id="product-page">14. Complete the app's product details</h2>
<p>On Google Play, your app's product information is shown to users on its
product details page, the page that users visit to learn more about your app and
@@ -431,6 +464,10 @@
page, make sure that you can enter or upload it to the Developer Console, until
the page is complete and ready for publishing. </p>
+<p>If your app is targeting tablet devices, make sure to include at least one screen
+shot of the app running on a tablet, and highlight your app's support for tablets
+in the app description, release notes, promotional campaigns, and elsewhere.</p>
+
<table>
<tr>
<td><p>Related resources:</p>
@@ -444,7 +481,7 @@
</tr>
</table>
-<h2 id="badges">14. Use Google Play badges and links in your promotional
+<h2 id="badges">15. Use Google Play badges and links in your promotional
campaigns</h2>
<p>Google Play badges give you an officially branded way of promoting your app
@@ -473,7 +510,7 @@
</tr>
</table>
-<h2 id="final-checks">15. Final checks and publishing</h2>
+<h2 id="final-checks">16. Final checks and publishing</h2>
<p>When you think you are ready to publish, sign in to the Developer Console and take a few moments for a few
final checks:</p>
@@ -511,7 +548,7 @@
</table>
-<h2 id="support">16. Support users after launch</h2>
+<h2 id="support">17. Support users after launch</h2>
<p>After you publish an app or an app update, it's crucial for you to support
your customers. Prompt and courteous support can provide a better experience for
diff --git a/docs/html/distribute/googleplay/quality/core.jd b/docs/html/distribute/googleplay/quality/core.jd
new file mode 100644
index 0000000..3d017e6
--- /dev/null
+++ b/docs/html/distribute/googleplay/quality/core.jd
@@ -0,0 +1,783 @@
+page.title=Core App Quality Guidelines
+@jd:body
+
+<div id="qv-wrapper"><div id="qv">
+<h2>Quality Criteria</h2>
+ <ol>
+ <li><a href="#ux">Design and Interaction</a></li>
+ <li><a href="#fn">Functionality</a></li>
+ <li><a href="#ps">Performance and Stability</a></li>
+ <li><a href="#listing">Google Play</a></li>
+
+ </ol>
+
+ <h2>Testing</h2>
+ <ol>
+ <li><a href="#test-environment">Setting Up a Test Environment</a></li>
+ <li><a href="#tests">Test Procedures</a></li>
+ </ol>
+
+ <h2>You Should Also Read</h2>
+ <ol>
+ <li><a href="{@docRoot}distribute/googleplay/quality/tablet.html">Tablet App Quality Checklist</a></li>
+ <li><a href="{@docRoot}distribute/googleplay/strategies/app-quality.html">Improving App Quality</a></li>
+ </ol>
+
+
+</div>
+</div>
+
+<p><em>App quality</em> directly influences the long-term success of your app
+— in terms of installs, user rating and reviews, engagement, and user
+retention. Android users expect high quality design and performance from apps,
+even more so if they puchased the apps or purchased their in-app products.</p>
+
+<p>This document helps you assess basic aspects of quality in your app through a
+compact set of <em>core app quality criteria</em> and associated tests. All
+Android apps should meet these criteria.</p>
+
+<p>Before launching your app, make sure to test it against these criteria to
+ensure that it functions well on many devices, meets Android standards for
+navigation and design, and is prepared for promotional opportunities in the
+Google Play Store. Your testing will go well beyond what's described here
+— the purpose of this document is to specify the essential characteristics
+of basic quality so that you can include them in your test plans.</p>
+
+<p>If your app is targeting tablet devices, make sure that it delivers a rich,
+compelling experience to your tablet customers. See the <a
+href="/distribute/googleplay/quality/tablet.html">Tablet App Quality
+Checklist</a> for recommendations on ways to optimize your app for tablets.</p>
+
+
+<h2 id="ux">Visual Design and User Interaction</h2>
+
+<p>These criteria ensure that your app provides standard Android visual design
+and interaction patterns where appropriate, for a consistent and intuitive
+user experience.</p>
+
+<table>
+ <tr>
+ <th style="width:2px;">
+ Area
+ </th>
+ <th style="width:54px;">
+ ID
+ </th>
+
+
+ <th>
+ Description
+ </th>
+ <th style="width:54px;">
+ Tests
+ </th>
+ </tr>
+ <tr id="cg7">
+ <td>Standard design</td>
+ <td>
+ UX-B1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App follows <a href="{@docRoot}design/index.html">Android Design</a> guidelines and uses common <a href="{@docRoot}design/patterns/index.html">UI patterns and icons</a>:</p>
+ <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+ <li>App does not redefine the expected function of a system icon (such as the Back button).</li>
+ <li>App does not replace a system icon with a completely different icon if it triggers the standard UI behavior. </li>
+ <li>If the app provides a customized version of a standard system icon, the icon strongly resembles the system icon and triggers the standard system behavior.</li>
+ <li>App does not redefine or misuse Android UI patterns, such that icons or behaviors could be misleading or confusing to users.</li>
+ </ol>
+ </td>
+ <td><a href="#core">CR-all</a></td>
+ </tr>
+
+
+ <tr>
+ <td rowspan="3">Navigation</td>
+ <td id="cn1">
+ UX-N1
+ </td>
+
+ <td>
+ <p>App supports standard system <a href="{@docRoot}design/patterns/navigation.html">Back button navigation</a> and does not make use of any custom, on-screen "Back button" prompts.</p>
+ </td>
+ <td><a href="#core">CR-3</a></td>
+ </tr>
+ <tr>
+ <td id="cn2">
+ UX-N2
+ </td>
+ <td>
+ <p>All dialogs are dismissable using the Back button.</p>
+ </td>
+ <td><a href="#core">CR-3</a></td>
+ </tr>
+
+ <tr id="cn4">
+ <td>
+ UX-N3
+ </td>
+ <td>
+ Pressing the Home button at any point navigates to the Home screen of the device.
+ </td>
+ <td><a href="#core">CR-1</a></td>
+ </tr>
+ </table>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}design/index.html">Android Design</a></strong> — Overview of design and user experience best practices for Android apps. </li>
+<li><strong><a href="{@docRoot}design/patterns/navigation.html">Navigation with Back and Up</a></strong> — Android Design document describing standard navigation patterns and related topics. </li>
+<li><strong><a href="{@docRoot}design/patterns/actionbar.html">Action Bar</a></strong> — Android Design document describing how to use the Action Bar. </li>
+<li><strong><a href="{@docRoot}design/style/iconography.html">Iconography</a></strong> — Android Design document that shows how to use various types of icons.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+<h2 id="fn">Functionality</h2>
+
+<p>These criteria ensure that your app provides expected functional behavior with the appropriate level of permissions. </p>
+
+<table>
+ <tr>
+ <th style="width:2px;">
+ Area
+ </th>
+ <th style="width:54px;">
+ ID
+ </th>
+
+
+ <th>
+ Description
+ </th>
+ <th style="width:54px;">
+ Tests
+ </th>
+ </tr>
+
+ <tr>
+ <td rowspan="2">Permissions</td>
+ <td>
+ FN-P1
+ </td>
+ <td>App requests only the <em>absolute minimum</em> permissions that it needs to support core functionality.
+ </td>
+ <td rowspan="2"><a href="#core">CR-11</a></td>
+ </tr>
+ <tr>
+ <td>
+ FN-P2
+ </td>
+ <td><p style="margin-bottom:.5em;">App does not request permissions to access sensitive data (such as Contacts or the System Log) or services that can cost the user money (such as the Dialer or SMS), unless related to a core capability of the app.
+ </td>
+ </tr>
+
+ <tr>
+ <td>Install location</td>
+ <td>
+ FN-L1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App functions normally when installed on SD card (if supported by app).</p>
+
+ <p style="margin-bottom:.25em;">Supporting installation to SD card is recommended for most large apps (10MB+). See the <a href="{@docRoot}guide/topics/data/install-location.html">App Install Location</a> developer guide for information about which types of apps should support installation to SD card.</p>
+ </td>
+
+ <td><a href="#SD-1">SD-1</a>
+ </td>
+ </tr>
+ <tr>
+ <td rowspan="4">Audio</td>
+ <td id="cg1">
+ FN-A1
+ </td>
+
+ <td>
+ Audio does not play when the screen is off, unless this is a core feature (for example, the app is a music player).
+ </td>
+ <td><a href="#core">CR-7</a></td>
+ </tr>
+ <tr id="cg2">
+ <td>
+ FN-A2
+ </td>
+ <td>
+ Audio does not <a href="http://android-developers.blogspot.com/2011/11/making-android-games-that-play-nice.html">play behind the lock screen</a>, unless this is a core feature.
+ </td>
+ <td><a href="#core">CR-8</a></td>
+ </tr>
+ <tr id="cg3">
+ <td>
+ FN-A3
+ </td>
+ <td>
+ Audio does not play on the home screen or over another app, unless this is a core feature.
+ </td>
+ <td><a href="#core">CR-1, <br />CR-2</a></td>
+ </tr>
+ <tr id="cg4">
+ <td>
+ FN-A4
+ </td>
+ <td>
+ Audio resumes when the app returns to the foreground, or indicates to the user that playback is in a paused state.
+ </td>
+ <td><a href="#core">CR-1, CR-8</a></td>
+ </tr>
+ <tr id="cg5">
+ <td rowspan="3">UI and Graphics</td>
+ <td>
+ FN-U1
+ </td>
+
+<td>
+ <p style="margin-bottom:.5em;">App supports both landscape and portrait orientations (if possible).</em></p>
+
+ <p style="margin-bottom:.25em;">Orientations expose largely the same features and actions and preserve functional parity.
+ Minor changes in content or views are acceptable.</p>
+ </td>
+ <td><a href="#core">CR-5</a></td>
+ </tr>
+ <tr id="cg5">
+ <td>
+ FN-U2
+ </td>
+
+<td>
+ <p style="margin-bottom:.5em;">App uses the whole screen in both orientations and does not letterbox to account for orientation changes.</em></p>
+ <p style="margin-bottom:.25em;">Minor letterboxing to compensate for small variations in screen geometry is acceptable.</p>
+ </td>
+ <td><a href="#core">CR-5</a></td>
+ </tr>
+ <tr id="cg7">
+ <td>
+ FN-U3
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App correctly handles rapid transitions between display orientations without rendering problems.</p>
+ </td>
+ <td><a href="#core">CR-5</a></td>
+ </tr>
+
+ <tr id="cg8">
+ <td rowspan="2">User/app state</td>
+ <td>
+ FN-S1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App should not leave any services running when the app is in the background, unless related to a core capability of the app.</p>
+ <p style="margin-bottom:.25em;">For example, the app should not leave services running to maintain a network connection for notifications, to maintain a Bluetooth connection, or to keep the GPS powered-on.</p>
+ </td>
+ <td><a href="#core">CR-6</a></td>
+ </tr>
+
+
+ <tr id="cn3">
+ <td>
+ FN-S2
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App correctly preserves and restores user or app state.</p>
+ <p style="margin-bottom:.25em;">App preserves user or app state when leaving the foreground and prevents accidental data loss due to back-navigation and other state changes. When returning to the foreground, the app must restore the preserved state and any significant stateful transaction that was pending, such as changes to editable fields, game progress, menus, videos, and other sections of the app or game.</p>
+ <ol style="margin-bottom:.25em;list-style-type:lower-alpha">
+ <li>When the app is resumed from the Recents app switcher, the app returns the user to the exact state in which it was last used.</li>
+ <li>When the app is resumed after the device wakes from sleep (locked) state, the app returns the user to the exact state in which it was last used.</li>
+ <li>When the app is relaunched from Home or All Apps, the app restores the app state as closely as possible to the previous state.</li>
+ <li>On Back keypresses, the app gives the user the option of saving any app or user state that would otherwise be lost on back-navigation.</li>
+ </ol>
+ </td>
+ <td><a href="#core">CR-1, CR-3, CR-5</a></td>
+ </tr>
+
+</table>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="http://android-developers.blogspot.com/2011/11/making-android-games-that-play-nice.html">Making Android Apps that Play Nice</a></strong> — Developer blog post discussing the audio lifecycle and expected audio behaviors for Android apps. </li>
+<li><strong><a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back Stack</a></strong> — Developer guide describing how to implement back-navigation.</li>
+<li><strong><a href="{@docRoot}training/basics/activity-lifecycle/recreating.html">Recreating an Activity</a></strong> — Android Training class the shows how to preserve and restore app state.</li>
+
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="ps">Performance and Stability</h2>
+
+<p>To ensure a high user rating, your app needs to perform well and stay
+responsive on all of the devices and form factors and screens that it is
+targeting. These criteria ensure that the app provides the basic performance,
+stability, and responsiveness expected by users.</p>
+
+<table>
+ <tr>
+ <th style="width:2px;">
+ Area
+ </th>
+ <th style="width:54px;">
+ ID
+ </th>
+ <th>
+ Description
+ </th>
+ <th style="width:54px;">
+ Tests
+ </th>
+ </tr>
+
+ <tr id="cg9">
+ <td>Stability</td>
+ <td>
+ PS-S1
+ </td>
+ <td>
+ App does not crash, force close, freeze, or otherwise function abnormally on any targeted device.
+ </td>
+ <td><a href="#core">CR-all</a>, <a href="#SD-1">SD-1</a>, <a href="#HA-1">HA-1</a></td>
+ </tr>
+
+ <tr>
+ <td rowspan="2">Performance</td>
+ <td id="cp1">
+ PS-P1
+ </td>
+ <td>
+ App loads quickly or provides onscreen feedback to the user (a progress indicator or similar cue) if the app
+ takes longer than two seconds to load.
+ </td>
+ <td>
+ <a href="#core">CR-all</a>, <a href="#SD-1">SD-1</a>
+ </td>
+ </tr>
+ <tr id="cp2">
+
+ <td>
+ PS-P2
+ </td>
+ <td>
+ With StrictMode enabled (see <a href="#strictmode">StrictMode Testing</a>, below), no red flashes (performance warnings from StrictMode) are visible when exercising the app, including
+ during game play, animations and UI transitions, and any other part of the app.
+ </td>
+ <td>
+ <a href="#PM-1">PM-1</a>
+ </td>
+ </tr>
+ <tr id="cp3">
+ <td>Media</td>
+ <td>
+ PS-M1
+ </td>
+ <td>
+ Music and video playback is smooth, without crackle, stutter, or other artifacts, during normal app usage and load.
+ </td>
+ <td>
+ <a href="#core">CR-all</a>, <a href="#SD-1">SD-1</a>, <a href="#HA-1">HA-1</a>
+ </td>
+ </tr>
+
+<tr id="cp4">
+ <td rowspan="2">Visual quality</td>
+ <td>
+ PS-V1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App displays graphics, text, images, and other UI elements without noticeable distortion, blurring, or pixelation.</p>
+
+ <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+ <li>App provides high-quality graphics for all targeted screen sizes and form factors, including for <a href="{@docRoot}distribute/googleplay/quality/tablet.html">larger-screen devices such as tablets</a>.</li>
+ <li>No aliasing at the edges of menus, buttons, and other UI elements.</li>
+ </ol>
+ </td>
+ <td rowspan="2"><a href="#core">CR-all</a></td>
+ </tr>
+
+ <tr id="cp5">
+ <td>
+ PS-V2
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App displays text and text blocks in an acceptable manner. </p>
+
+ <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+ <li>Composition is acceptable in all supported form factors, including for larger-screen devices such as tablets.</li>
+ <li>No cut-off letters or words.</li>
+ <li>No improper word wraps within buttons or icons.</li>
+ <li>Sufficient spacing between text and surrounding elements.</li>
+ </ol>
+
+
+
+ </td>
+
+ </tr>
+
+
+
+
+</table>
+
+
+
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="http://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html">Using StrictMode</a></strong> — Developer blog post discussing StrictMode and how to use it for performance monitoring in your app. </li>
+<li><strong><a href="{@docRoot}guide/practices/responsiveness.html">Designing for Responsiveness</a></strong> — Developer guide describing best practices for keeping your app responsive.</li>
+<li><strong><a href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading for Performance</a></strong> — Developer blog post discussing ways to improve performance through multi-threading.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="listing">Google Play</h2>
+
+<p>To launch your app successfully on Google Play, raise its ratings, and make
+sure that it is ready for promotional activities in the store, follow the
+criteria below.</p>
+
+<table>
+ <tr>
+ <th style="width:2px;">
+ Area
+ </th>
+ <th style="width:54px;">
+ ID
+ </th>
+ <th>
+ Description
+ </th>
+ <th style="width:54px;">
+ Tests
+ </th>
+ </tr>
+<tr>
+<td rowspan="2">Policies</td>
+ <td>
+ GP-P1
+ </td>
+ <td>
+ App strictly adheres to the terms of the <a href="http://play.google.com/about/developer-content-policy.html">Google Play Developer Content Policy</a> and does not offer inappropriate content, does not use intellectual property or brand of others, and so on.
+ </td>
+ <td>
+ <a href="#gp">GP-all</a>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td>
+ GP-P2
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App maturity level is set appropriately, based on the
+ <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=188189">Content Rating Guidelines</a>.</p>
+
+ <p style="margin-bottom:.25em;">Especially, note that apps that request permission to use the device location cannot be given the maturity level "Everyone". </p>
+ </td>
+ <td>
+ <a href="#gp">GP-1</a>
+ </td>
+ </tr>
+
+
+ <tr>
+
+ <td rowspan="3">App Details Page</td>
+ <td>
+ GP-D1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">App feature graphic follows the guidelines outlined in this
+ <a href="http://android-developers.blogspot.com/2011/10/android-market-featured-image.html">blog post</a>. Make sure that:</p>
+
+ <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+ <li>The app listing includes a high-quality feature graphic.</li>
+ <li>The feature graphic does not contain device images, screenshots, or small text that will be illegible when scaled down and displayed on the smallest screen size that your app is targeting.</li>
+ <li>The feature graphic does not resemble an advertisement.</li>
+ </ol>
+
+
+ </td>
+
+ <td>
+ <a href="#gp">GP-1, GP-2</a>
+ </td>
+
+
+ </tr>
+ <tr>
+ <td>
+ GP-D2
+ </td>
+ <td>
+ App screenshots and videos do not show or reference non-Android devices.
+ </td>
+ <td rowspan="2"><a href="#gp">GP-1</a></td>
+ </tr>
+
+ <tr>
+ <td>
+ GP-D3
+ </td>
+ <td>
+ App screenshots or videos do not
+ represent the content and experience of your app in a misleading way.
+ </td>
+ </tr>
+ <tr>
+ <td>User Support</td>
+ <td>
+ GP-X1
+ </td>
+ <td>Common user-reported bugs in the Reviews tab of the Google Play page are addressed if they are
+ reproducible and occur on many different devices. If a bug occurs on only a few devices,
+ you should still address it if those devices are particularly popular or new.
+ </td>
+
+ <td>
+ <a href="#gp">GP-1</a>
+ </td>
+
+ </tr>
+</table>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="http://play.google.com/about/developer-content-policy.html">Google Play Developer Program Policies</a></strong> — Guidelines for what is acceptable conent in Google Play. Please read and understand the and understand the policies before publishing.
+<li><strong><a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=188189">Rating your application content for Google Play</a></strong> — Help Center document describing content ratings levels and how to choose the appropriate one for your app.</li>
+<li><strong><a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1078870">Graphic Assets for your Application
+</a></strong> — Details about the graphic assets you need to upload before publishing.</li>
+<li><strong><a href="http://android-developers.blogspot.com/2011/10/android-market-featured-image.html">Google Play Featured Image Guidelines
+</a></strong> — Blog post discussing how to create an attractive, effective Featured Image for your app.</li>
+<li><strong><a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113477&topic=2364761&ctx=topic">Supporting your users
+</a></strong> — Help Center document describing options for supporting users.</li>
+</ul>
+</td></tr>
+</table>
+
+
+<h2 id="test-environment">Setting Up a Test Environment</h2>
+
+<p>To assess the quality of your app, you need to set up a suitable
+hardware or emulator environment for testing. </p>
+
+<p>The ideal test environment would
+include a small number of actual hardware devices that represent key form
+factors and hardware/software combinations currently available to consumers.
+It's not necessary to test on <em>every</em> device that's on the market —
+rather, you should focus on a small number of representative devices, even using
+one or two devices per form factor. </p>
+
+<p>If you are not able to obtain actual hardware devices for testing, you should
+set up emulated devices (AVDs) to represent the most common form factors and
+hardware/software combinations. </p>
+
+<p>To go beyond basic testing, you can add more devices, more form factors, or
+new hardware/software combinations to your test environment. You can also
+increase the number or complexity of tests and quality criteria. </p>
+
+
+<h2 id="tests">
+ Test Procedures
+</h2>
+
+<p>These test procedures help you discover various types of quality issues in your app. You can combine the tests or integrate groups of tests together in your own test plans. See the sections above for references that associate specific criteria with specific tests. </p>
+
+<table>
+ <tr>
+ <th style="width:2px;">
+ Type
+ </th>
+ <th style="width:54px;">
+ Test
+ </th>
+ <th>
+ Description
+ </th>
+ </tr>
+ <tr>
+ <td rowspan="11" id="core">Core Suite</td>
+ <td>
+ CR-0
+ </td>
+ <td><p style="margin-bottom:.5em;">Navigate to all parts of the app — all screens, dialogs, settings, and all user flows. </p>
+
+ <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+ <li>If the application allows for editing or content creation, game play, or media playback, make sure to enter those flows to create or modify content.</li>
+ <li>While exercising the app, introduce transient changes in network connectivity, battery function, GPS or location availability, system load, and so on. </li>
+ </ol>
+ </td>
+ </tr>
+ <tr id="tg2">
+ <td id="core">
+ CR-1
+ </td>
+ <td>From each app screen, press the device's Home key, then re-launch the app from the All Apps screen.
+ </td>
+ </tr>
+ <tr id="CR-2">
+ <td>
+ CR-2
+ </td>
+ <td>From each app screen, switch to another running app and then return to the app under test using the Recents app switcher.
+ </td>
+ </tr>
+
+ <tr id="CR-3">
+ <td>
+ CR-3
+ </td>
+ <td>From each app screen (and dialogs), press the Back button.
+ </td>
+ </tr>
+ <tr id="CR-5">
+ <td>
+ CR-5
+ </td>
+ <td>From each app screen, rotate the device between landscape and portrait orientation at least three times.
+ </td>
+ </tr>
+ <tr id="CR-6">
+ <td>
+ CR-6
+ </td>
+ <td>Switch to another app to send the test app into the background. Go to Settings and check whether the test app has any services running while in the background. In Android 4.0 and higher, go to the Apps screen and find the app in the "Running" tab. In earlier versions, use "Manage Applications" to check for running services.
+ </td>
+ </tr>
+
+
+ <tr id="CR-7">
+ <td>
+ CR-7
+ </td>
+ <td>
+ Press the power button to put the device to sleep, then press the power button again to
+ awaken the screen.
+ </td>
+ </tr>
+ <tr id="CR-8">
+ <td>
+ CR-8
+ </td>
+ <td>
+ Set the device to lock when the power button is pressed. Press the power button to put the device to sleep, then press the power button again to
+ awaken the screen, then unlock the device.
+ </td>
+ </tr>
+ <tr id="CR-9">
+ <!-- Hardware features -->
+ <td>
+ CR-9
+ </td>
+ <td>
+ For devices that have slide-out keyboards, slide the keyboard in and out at least once. For devices that have keyboard docks, attach the device to the keyboard dock.
+ </td>
+ </tr>
+ <tr id="CR-10">
+ <td>
+ CR-10
+ </td>
+ <td>
+ For devices that have an external display port, plug-in the external display.
+ </td>
+ </tr>
+ <tr id="CR-11">
+ <td>
+ CR-11
+ </td>
+ <td>Examine the permissions requested by the app by going to Settings > App Info.
+ </td>
+ </tr>
+ <tr id="tg3">
+ <td>Install on SD Card</td>
+ <td>
+ SD-1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">Repeat <em>Core Suite</em> with app installed to <a href="{@docRoot}guide/topics/data/install-location.html">device SD card</a> (if supported by app).</p>
+
+ <p style="margin-bottom:.25em;">To move the app to SD card, you can use Settings > App Info > Move to SD Card.</p>
+ </td>
+ </tr>
+
+
+
+ <tr id="tg3">
+ <td>Hardware acceleration</td>
+ <td>
+ HA-1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">Repeat <em>Core Suite</em> with hardware acceleration enabled.</p>
+
+ <p style="margin-bottom:.25em;">To force-enable hardware acceleration (where supported by device), add <code>hardware-accelerated="true"</code> to the <code><application></code> in the app manifest and recompile.</p>
+ </td>
+ </tr>
+
+ <tr id="tg3">
+ <td>Performance Monitoring</td>
+ <td>
+ PM-1
+ </td>
+ <td>
+ <p style="margin-bottom:.5em;">Repeat <em>Core Suite</em> with StrictMode profiling enabled <a href="#strictmode">as described below</a>. <p style="margin-bottom:.25em;">Pay close attention to garbage collection and its impact on the user experience.</p>
+ </td>
+ </tr>
+
+ <tr id="tg4">
+ <td rowspan="3">Google Play</td>
+ <td>
+ GP-1
+ </td>
+ <td>
+ Sign into the <a href="https://play.google.com/apps/publish/">Developer Console</a> to review your developer profile, app description, screenshots, feature graphic, maturity settings, and user feedback.
+ </td>
+ </tr>
+ <tr id="tg4">
+ <td>
+ GP-2
+ </td>
+ <td>
+ Download your feature graphic and screenshots and scale them down to match the display sizes on the devices and form factors you are targeting.
+ </td>
+ </tr>
+ <tr id="tg4">
+ <td>
+ GP-4
+ </td>
+ <td>
+ Review all graphical assets, media, text, code libraries, and other content packaged in the app or expansion file download.
+ </td>
+ </tr>
+ <tr id="tg4">
+ <td>Payments</td>
+ <td>
+ GP-5
+ </td>
+ <td>
+ Navigate to all screens of your app and enter all in-app purchase flows.
+ </td>
+</tr>
+
+</table>
+
+<h3 id="strictmode">
+Testing with StrictMode
+</h3>
+
+<p>For performance testing, we recommend enabling <code><a href="{@docRoot}reference/android/os/StrictMode.html">StrictMode</a></code> in your app and using it to catch operations on the main thread and other threads that could affect performance, network accesses, file reads/writes, and so on.
+
+You can set up a monitoring policy per thread using the <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html">ThreadPolicy builder</a></code> and enable all supported monitoring in the <code>ThreadPolicy</code> using <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html#detectAll()">detectAll()</a></code>.</p>
+
+<p>Make sure to enable <strong>visual notification</strong> of policy violations for the <code>ThreadPolicy</code> using <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html#penaltyFlashScreen()">penaltyFlashScreen()</a></code>.</p>
+
diff --git a/docs/html/distribute/googleplay/quality/index.jd b/docs/html/distribute/googleplay/quality/index.jd
new file mode 100644
index 0000000..ffbcbab
--- /dev/null
+++ b/docs/html/distribute/googleplay/quality/index.jd
@@ -0,0 +1,46 @@
+page.title=App Quality
+@jd:body
+
+<p><em>App quality</em> directly influences the long-term success of your app
+— in terms of installs, user rating and reviews, engagement, and user
+retention. Android users expect high quality from apps, even more so if they
+puchased the apps or purchased their in-app products. At the same time, they
+enjoy and value apps that put a priority on high quality and continuous
+improvement.</p>
+
+<p>Before you publish an app on Google Play, it's important to make sure that
+the app meets the basic quality expectations of users, across all of the form
+factors and device types that the app is targeting. The documents in this
+section help you assess your app's fundamental quality and they provide
+resources to help you address issues that you find. </p>
+
+<div class="vspace size-1">
+
+</div>
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+ <h4>
+ Core App Quality
+ </h4>
+ <p>
+ A set of core quality criteria that all Android apps should meet on all targeted devices.
+ </p><a href="{@docRoot}distribute/googleplay/quality/core.html">Learn more »</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Tablet App Quality
+ </h4>
+ <p>
+ A set recommendations for delivering the best possible experience to tablet users.
+ </p><a href="{@docRoot}distribute/googleplay/quality/tablet.html">Learn more »</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Improving App Quality
+ </h4>
+ <p>
+ Tips on continuously improving your app's quality, ratings, reviews, downloads, and engagement.
+ </p><a href="{@docRoot}distribute/googleplay/strategies/app-quality.html">Learn more
+ »</a>
+ </div>
+</div>
diff --git a/docs/html/distribute/googleplay/quality/tablet.jd b/docs/html/distribute/googleplay/quality/tablet.jd
new file mode 100644
index 0000000..b8a7a60
--- /dev/null
+++ b/docs/html/distribute/googleplay/quality/tablet.jd
@@ -0,0 +1,555 @@
+page.title=Tablet App Quality Checklist
+@jd:body
+
+<div id="qv-wrapper"><div id="qv">
+<h2>Checklist</h2>
+<ol>
+
+<li><a href="#core-app-quality">1. Test for Core App Quality</a></li>
+<li><a href="#optimize-layouts">2. Optimize your layouts</a></li>
+<li><a href="#use-extra-space">3. Use the extra screen area</a></li>
+<li><a href="#use-tablet-icons">4. Use assets designed for tablets</a></li>
+<li><a href="#adjust-font-sizes">5. Adjust fonts and touch targets</a></li>
+<li><a href="#adjust-widgets">6. Adjust homescreen widgets</a></li>
+<li><a href="#offer-full-feature-set">7. Offer the app's full feature set</a></li>
+<li><a href="#hardware-requirements">8. Don’t require hardware features</a></li>
+<li><a href="#support-screens">9. Declare tablet screen support</a></li>
+<li><a href="#google-play">10. Follow best practices for publishing in Google Play</a></li>
+
+</ol>
+<h2>Testing</h2>
+<ol>
+<li><a href="#test-environment">Setting Up a Test Environment</a></li>
+</ol>
+</div></div>
+
+
+<p>Before you publish an app on Google Play, it's important to make sure that
+the app meets the basic expectations of tablet users, through compelling features
+and an intuitive, well-designed UI. </p>
+
+<p>Tablets are a growing part of the Android installed base that offers new
+opportunities for <a
+href="{@docRoot}distribute/googleplay/spotlight/tablets.html">user engagement
+and monetization</a>. If your app is targeting tablet users, this document helps
+you focus on key aspects of quality, feature set, and UI that can have a
+significant impact on the app's success. Each focus area is given as checklist
+item, with each one comprising several smaller tasks or best practices.</p>
+
+<p>Although the checklist tasks below are numbered for convenience,
+you can handle them in any order and address them to the extent that you feel
+is right for your app. In the interest of delivering the best possible product
+to your customers, follow the checklist recommendations
+to the greatest extent possible. </p>
+
+<p>As you move through the checklist, you'll find links to support resources
+that can help you address the topics raised in each task.</p>
+
+
+<h2 id="core-app-quality">1. Test for Core App Quality</h2>
+
+<p>The first step in delivering a great tablet app experience is making sure
+that it meets the <em>core app
+quality criteria</em> for all of the devices and form factors that the app is
+targeting. For complete information, see the <a
+href="{@docRoot}distribute/googleplay/quality/core.html">Core App Quality Checklist</a>.
+</p>
+
+<p>To assess the quality of your app on tablets — both for core app quality
+and tablet app quality — you need to set up a suitable
+hardware or emulator environment for testing. For more information,
+see <a href="#test-environment">Setting Up a Test Environment</a>.</p>
+
+
+<h2 id="optimize-layouts">2. Optimize your layouts for larger screens</h2>
+
+<p>Android makes it easy to develop an app that runs well on a wide range of
+device screen sizes and form factors. This broad compatibility works in your
+favor, since it helps you design a single app that you can distribute widely to
+all of your targeted devices. However, to give your users the best possible
+experience on each screen configuration — in particular on tablets
+— you need to optimize your layouts and other UI components for each
+targeted screen configuration. On tablets, optimizing your UI lets you take
+full advantage of the additional screen available, such as to offer new features,
+present new content, or enhance the experience in other ways to deepen user
+engagement.</p>
+
+<p>If you developed your app for handsets and now want to distribute it to
+tablets, you can start by making minor adjustments to your layouts, fonts, and
+spacing. In some cases — such as for 7-inch tablets or for a game with
+large canvas — these adjustments may be all
+you need to make your app look great. In other cases, such as for larger
+tablets, you can redesign parts of your UI to replace "stretched UI" with an
+efficient multipane UI, easier navigation, and additional content. </p>
+
+<p>Here are some suggestions:</p>
+
+<div style="width:390px;float:right;margin:1.5em;margin-top:0em;">
+<img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-bad.png" style="width:390px;padding:4px;margin-bottom:0em;">
+<p class="image-caption" style="padding:0em .5em .5em 2em"><span
+style="font-weight:500;">Get rid of "stretched" UI</span>: On tablets, single-pane layouts lead to awkward whitespace and excessive line lengths. Use padding to reduce the width of UI elements and consider using multi-pane layouts.</p>
+</div>
+
+<ul>
+<li>Provide custom layouts as needed for <code>large</code> and
+<code>xlarge</code> screens. You can also provide layouts that are loaded based
+on the screen's <a href="{@docRoot}guide/practices/screens_support.html#NewQualifiers">shortest
+dimension</a> or the <a href="{@docRoot}guide/practices/screens_support.html#NewQualifiers">minimum
+available width and height</a>. </li>
+<li>At a minimum, customize dimensions such as font sizes, margins, spacing for
+larger screens, to improve use of space and content legibility. </li>
+<li>Adjust positioning of UI controls so that they are easily accessible to
+users when holding a tablet, such as toward the sides when in
+landscape orientation.</li>
+<li>Padding of UI elements should normally be larger on tablets than on handsets. A
+<a href="{@docRoot}design/style/metrics-grids.html#48dp-rhythm">48dp rhythm</a> (and a 16dp
+grid) is recommended.</li>
+<li>Adequately pad text content so that it is not aligned directly along screen edges.
+Use a minimum <code>16dp</code> padding around content near screen edges.</li>
+</ul>
+
+<p>In particular, make sure that your layouts do not appear "stretched"
+across the screen:</p>
+
+<ul>
+<li>Lines of text should not be excessively long — optimize for a maximum
+100 characters per line, with best results between 50 and 75.</li>
+<li>ListViews and menus should not use the full screen width.</li>
+<li>Use padding to manage the widths of onscreen elements or switch to a
+multi-pane UI for tablets (see next section).</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="http://developer.android.com/design/style/metrics-grids.html">Metrics and Grids
+</a></strong> — Android Design document that explains ....</li>
+<li><strong><a href="http://developer.android.com/design/style/devices-displays.html">Devices and Displays
+</a></strong> — Android Design document that explains ....</li>
+<li><strong><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></strong> — Developer documentation that explains the details of managing UI for best display on multiple screen sizes.</li>
+<li><strong><a href="http://developer.android.com/guide/practices/screens_support.html#ConfigurationExamples">Configuration examples
+</a></strong> — Examples of how to declare layouts and other resources for specific screen sizes.</a></li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="use-extra-space">3. Take advantage of extra screen area available on tablets</h2>
+
+<div style="width:290px;float:right;margin:1.5em;margin-bottom:0;margin-top:0;">
+<img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-good.png" style="width:280px;padding:4px;margin-bottom:0em;">
+<p class="image-caption" style="padding:0em .5em .5em 1.5em"><span
+style="font-weight:500;">Multi-pane layouts</span> result in a better visual balance on tablet screens, while offering more utility and legibility.</p>
+</div>
+
+<p>Tablet screens provide significantly more screen real estate to your app,
+especially when in landscape orientation. In particular, 10-inch tablets offer a
+greatly expanded area, but even 7-inch tablets give you more space for
+displaying content and engaging users. </p>
+
+<p>As you consider the UI of your app when running on tablets, make sure that it
+is taking full advantage of extra screen area available on tablets. Here are
+some suggestions:</p>
+
+<ul>
+<li>Look for opportunities to include additional content or use an alternative
+treatment of existing content.</li>
+<li>Use <a href="{@docRoot}design/patterns/multi-pane-layouts.html">multi-pane
+layouts</a> on tablet screens to combine single views into a compound view. This
+lets you use the additional screen area more efficiently and makes it easier for
+users to navigate your app. </li>
+<li>Plan how you want the panels of your compound views to reorganize when
+screen orientation changes.</li>
+
+
+
+<div style="width:490px;margin:1.5em auto 1.5em 0;">
+
+<div style="">
+<img src="{@docRoot}images/ui-ex-single-panes.png" style="width:490px;padding:4px;margin-bottom:0em;" align="middle">
+<img src="{@docRoot}images/ui-ex-multi-pane.png" style="width:490px;padding:4px;margin-bottom:0em;">
+<p class="image-caption" style="padding:.5em"><span
+style="font-weight:500;">Compound views</span> combine several single views from a handset UI <em>(above)</em> into a richer, more efficient UI for tablets <em>(below)</em>. </p>
+</div>
+</div>
+
+<li>While a single screen is implemented as an {@link android.app.Activity}
+subclass, consider implementing individual content panels as {@link
+android.app.Fragment} subclasses. This lets you maximize code reuse across
+different form factors and across screens that share content.</li>
+<li>Decide on which screen sizes you'll use a multi-pane UI, then provide the
+different layouts in the appropriate screen size buckets (such as
+<code>large</code>/<code>xlarge</code>) or minimum screen widths (such as
+<code>sw600dp</code>/<code>sw720</code>).</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}design/patterns/multi-pane-layouts.html">Multi-pane Layouts</a></strong> — Android Design guide for using multi-pane UI, including examples of how to flatten navigation and integrate more content into your tablet UI.</li>
+<li><strong><a href="{@docRoot}training/design-navigation/multiple-sizes.html">Planning for Multiple Touchscreen Sizes</a></strong> — Android Training class that walks you through the essentials of planning an intuitive, effective navigation for tablets and other devices. </li>
+<li><strong><a href="{@docRoot}training/multiscreen/index.html">Designing for Multiple Screens</a></strong> — Android Training class that walks you through the essentials of planning an intuitive, effective navigation for tablets and other devices. </li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="use-tablet-icons">4. Use Icons and other assets that are designed for tablet screens</h2>
+
+<p>So that your app looks its best, make sure to use icons and other bitmap
+assets that are created specifically for the densities used by tablet screens.
+Specifically, you should create sets of alternative bitmap drawables for each
+density in the range commonly supported by tablets.</p>
+
+<p class="table-caption">Raw asset sizes for icon types.<table>
+<tr>
+<th>Density </th>
+<th colspa>Launcher</th>
+<th>Action Bar</th>
+<th>Small/Contextual</th>
+<th>Notification</th>
+</tr>
+<tr>
+<td><code>mdpi</code></td>
+<td>48x48px</td>
+<td>32x32px</td>
+<td>16x16px</td>
+<td>24x24px</td>
+</tr>
+<tr>
+<td><code>hdpi</code></td>
+<td>72x72px</td>
+<td>48x48px</td>
+<td>24x24px</td>
+<td>36x36px</td>
+</tr>
+<tr>
+<td><code>tvdpi</code></td>
+<td><em>(use hdpi)</em></td>
+<td><em>(use hdpi)</em></td>
+<td><em>(use hdpi)</em></td>
+<td><em>(use hdpi)</em></td>
+</tr>
+<tr>
+<td><code>xhdpi</code></td>
+<td>96x96px</td>
+<td>64x64px</td>
+<td>32x32px</td>
+<td>48x48px</td>
+</tr>
+
+</table>
+
+<p>Other points to consider: </p>
+
+<ul>
+<li>Icons in the action bar, notifications, and launcher should be designed
+according to the icon design guidelines and have the same physical size on
+tablets as on phones.</li>
+<li>Use density-specific <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
+resource qualifiers</a> to ensure that the proper set of alternative resources
+gets loaded.</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}design/style/iconography.html">Iconography</a></strong> — Android Design document that shows how to use various types of icons.</li>
+<li><strong><a href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a></strong> — Developer documentation on how to provide sets of layouts and drawable resources for specific ranges of device screens. </li>
+<li><strong><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></strong> — API Guide documentation that explains the details of managing UI for best display on multiple screen sizes.</li>
+<li><strong><a href="{@docRoot}training/basics/supporting-devices/screens.html">Supporting Different Screens</a></strong> — Android Training class that takes you through the process of optimizing the user experience for different screen sizes and densities.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="adjust-font-sizes">5. Adjust font sizes and touch targets for tablet screens</h2>
+
+<p>To make sure your app is easy to use on tablets, take some time to adjust the
+font sizes and touch targets in your tablet UI, for all of the screen
+configurations you are targeting. You can adjust font sizes through <a
+href="{@docRoot}guide/topics/ui/themes.html">styleable attributes</a> or <a
+href="{@docRoot}guide/topics/resources/more-resources.html#Dimension">dimension
+resources</a>, and you can adjust touch targets through layouts and bitmap
+drawables, as discussed above. </p>
+
+<p>Here are some considerations:</p>
+<ul>
+<li>Text should not be excessively large or small on tablet screen sizes and
+densities. Make sure that labels are sized appropriately for the UI elements they
+correspond to, and ensure that there are no improper line breaks in labels,
+titles, and other elements.</li>
+<li>The recommended touch-target size for onscreen elements is 48dp (32dp
+minimum) — some adjustments may be needed in your tablet UI. Read <a
+href="http://developer.android.com/design/style/metrics-grids.html">Metrics and
+Grids
+</a> to learn about implementation strategies to help most of your users. To
+meet the accessibility needs of certain users, it may be appropriate to use
+larger touch targets. </li>
+<li>When possible, for smaller icons, expand the touchable area to more than
+48dp using {@link android.view.TouchDelegate} or just centering the icon within
+the transparent button.</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="http://developer.android.com/design/style/metrics-grids.html">Metrics and Grids
+</a></strong> — Android Design document that explains how to arrange and size touch targets and other UI elements on the screen.</li>
+<li><strong><a href="{@docRoot}design/style/typography.html">Typography</a></strong> — Android Design document that gives an overview of how to use typography in your apps. </li>
+<li><strong><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></strong> — Developer documentation that explains the details of managing UI for best display on multiple screen sizes.</li>
+<li><strong><a href="{@docRoot}training/multiscreen/screendensities.html">Supporting Different Densities</a></strong> — Android Training class that shows you how to provide sets of layouts and drawable resources for specific ranges of device screens. </li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="adjust-widgets">6. Adjust sizes of home screen widgets for tablet screens</h2>
+
+<p>If your app includes a home screen widget, here are a few points to consider
+to ensure a great user experience on tablet screens: </p>
+
+<ul>
+<li>Make sure that the widget's default height and width are set appropriately
+for tablet screens, as well as the minimum and maximum resize height and width.
+</li>
+<li>The widget should be resizable to 420dp or more, to span 5 or more home
+screen rows (if this is a vertical or square widget) or columns (if this is a
+horizontal or square widget). </li>
+<li>Make sure that 9-patch images render correctly.</li>
+<li>Use default system margins.</li>
+<li>Set the app's <code>targetSdkVersion</code> to 14 or higher, if
+possible.</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}guide/topics/appwidgets/index.html#MetaData">Adding the AppWidgetProviderInfo Metadata
+</a></strong> — API Guide that explains how to set the height and width dimensions of a widget.</li>
+<li><strong><a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget Design Guidelines</a></strong> — API Guide that provides best practices and techniques for designing and managing the size of widgets. </li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="offer-full-feature-set">7. Offer the app's full feature set to tablet users</h2>
+
+<p>Let your tablet users experience the best features of your app. Here are
+some recommendations:</p>
+
+<ul>
+<li>Design your app to offer at least the same set of features on tablets as it does on
+handsets. </li>
+<li>In exceptional cases, your app might omit or replace certain features on
+tablets if they are not supported by the hardware or use-case of most tablets.
+For example:
+<ul>
+<li>If the handset uses telephony features but telephony is not available on the
+current tablet, you can omit or replace the related functionality.</li>
+<li>Many tablets have a GPS sensor, but most users would not normally carry
+their tablets while running. If your phone app provides functionality to let the
+user record a GPS track of their runs while carrying their phones, the app would not need to
+provide that functionality on tablets because the use-case is not
+compelling.</li>
+</ul>
+</li>
+<li>If you will omit a feature or capability from your tablet UI, make sure
+that it is not accessible to users or that it offers “graceful degradation”
+to a replacement feature (also see the section below on hardware features).</li>
+</ul>
+
+
+<h2 id="hardware-requirements">8. Don’t require hardware features that might not be available on tablets</h2>
+
+<p>Handsets and tablets typically offer slightly different hardware support for
+sensors, camera, telephony, and other features. For example, many tablets are
+available in a "Wi-Fi" configuration that does not include telephony support.</p>
+
+<p>To ensure that you can deliver a single APK broadly across the
+your full customer base, make sure that your app does not have built-in
+requirements for hardware features that aren't commonly available on tablets.
+</p>
+
+<ul>
+<li>Your app's manifest should not include <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a>
+elements for hardware features or capabilities that might not be
+available on tablets, except when they are declared with the
+<code>android:required=”false”</code> attribute. For example, your app should
+not <em>require</em> features such as:
+<ul>
+<li><code>android.hardware.telephony</code></li>
+<li><code>android.hardware.camera</code> (refers to back camera), or</li>
+<li><code>android.hardware.camera.front</code></li>
+</ul>
+</li>
+<li>Similarly, your app manifest should not include any <a
+href="{@docRoot}guide/topics/manifest/permission-element.html"><code><permission></code></a> elements that <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions">imply
+feature requirements</a> that might not be appropriate for tablets, except when
+accompanied by a corresponding <code><uses-feature></code> element
+declared with the <code>android:required=”false”</code> attribute.</li>
+</ul>
+
+<p>In all cases, the app must function normally when the hardware features it
+uses are not available and should offer “graceful degradation” and alternative
+functionality where appropriate. For example, if GPS is not supported on the device,
+your app could let the user set their location manually. The app should do
+run-time checking for the hardware capability that it needs and handle as needed.</p>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions">Permissions that Imply Feature Requirements</a></strong> — A list of permissions that may cause unwanted filtering if declared in your app's manifest.</li>
+<li><strong><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a></strong> — Description and reference documentation for the <code><uses-feature></code> manifest element.</li>
+<li><strong><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#testing">Testing the features required by your application</a></strong> — Description of how to determine the actual set of hardware and software requirements (explicit or implied) that your app requires.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="support-screens">9. Declare support for tablet screen configurations</h2>
+
+<p>To ensure that you can distribute your app to a broad range of tablets,
+declare all the screen sizes that your app supports in its manifest:</p>
+
+<ul>
+<li>Declare a <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code><supports-screens></code></a> element
+with appropriate attributes, as needed.</li>
+<li>If the app declares a <code><compatible-screens></code> element in the
+manifest, the element must include attributes that specify <em>all of the size and
+density combinations for tablet screens</em> that the app supports. Note that, if possible,
+you should avoid using this element in your app.</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code><supports-screens></code></a></strong>
+— Description and reference documentation for the <code><supports-screens></code>
+manifest element.</li>
+<li><strong><a href="{@docRoot}guide/practices/screens_support.html#DeclaringScreenSizeSupport">Declaring Screen Size
+Support</a></strong> — Developer documentation that explains the details of managing UI
+for best display on multiple screen sizes.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="google-play">10. Follow best practices for publishing in Google Play</h2>
+
+<ul>
+<li>Publish your app as a single APK for all screen sizes (handsets
+and tablets), with a single Google Play listing:
+ <ul style="margin-top:.25em;">
+ <li>Easier for users to find your app from search, browsing, or promotions</li>
+ <li>Easier for users to restore your app automatically if they get a new device.</li>
+ <li>Your ratings and download stats are consolidated across all devices.</li>
+ <li>Publishing a tablet app in a second listing can dilute ratings for your brand.</li>
+ </ul>
+</li>
+<li>If necessary, you can alternatively choose to deliver your app using <a
+href="{@docRoot}guide/google/play/publishing/multiple-apks.html">Multiple APK Support</a>,
+although in most cases using a single APK to reach all devices is strongly recommended.</li>
+
+<li>Highlight your app’s tablet capabilities in the product details page:
+ <ul style="margin-top:.25em;">
+ <li>Add <strong>at least one screenshot taken while the app is running on a
+ tablet</strong>. It's recommended that you add one screenshot of landscape orientation
+ and one of portrait orientation, if possible. These screenshots make it clear to users
+ that your app is designed for tablets and highlight all the effort you've put into designing
+ a great tablet app experience.</li>
+ <li>Mention tablet support in the app description.</li>
+ <li>Include information about tablet support in the app's release notes and update
+ information.</li>
+ <li>In your app's promo video, add shots of your app running on a tablet.</li>
+ </ul>
+</li>
+<li>Make sure you are distributing to tablet devices. Check the app's Supported Devices
+list in the <a href="https://play.google.com/apps/publish/">Developer Console</a>
+to make sure your app is not filtered from tablet devices that you want to target.</li>
+
+<li>Let tablet users know about your app! Plan a marketing or advertising campaign that
+highlights the use of your app on tablets.</li>
+</ul>
+
+<table>
+<tr>
+<td><p>Related resources:</p>
+<ul style="margin-top:-.5em;">
+<li><strong><a href="https://play.google.com/apps/publish/">Google Play Android Developer Console</a></strong> — The tools console for publishing your app to Android users.</li>
+</ul>
+</td>
+</tr>
+</table>
+
+<h2 id="test-environment">Setting Up a Test Environment for Tablets</h2>
+
+<p>To assess the quality of your app on tablets — both for core app quality
+and tablet app quality — you need to set up a suitable
+hardware or emulator environment for testing. </p>
+
+<p>The ideal test environment would
+include a small number of actual hardware devices that represent key form
+factors and hardware/software combinations currently available to consumers.
+It's not necessary to test on <em>every</em> device that's on the market —
+rather, you should focus on a small number of representative devices, even using
+one or two devices per form factor. The table below provides an overview of
+devices you could use for testing.</p>
+
+<p>If you are not able to obtain actual hardware devices for testing, you should
+set up emulated devices (AVDs) to represent the most common form factors and
+hardware/software combinations. See the table below for suggestions on the emulator
+configurations to use. </p>
+
+<p>To go beyond basic testing, you can add more devices, more form factors, or
+new hardware/software combinations to your test environment. For example, you
+could include mid-size tablets, tablets with more or fewer hardware/software
+features, and so on. You can also increase the number or complexity of tests
+and quality criteria. </p>
+
+<p class="table-caption"><strong>Table 1</strong>. A typical tablet test environment might
+include one or two devices from each row in the table below, with one of the
+listed chipsets, platform versions, and hardware feature configurations.</p>
+
+<table>
+<tr>
+<th>Type</th>
+<th>Size</th>
+<th>Density</th>
+<th>Version</th>
+<th>AVD Skin</th>
+</tr>
+
+<tr>
+<td>7-inch tablet</td>
+<td><span style="white-space:nowrap"><code>large</code> or</span><br /><code>-sw600</code></td>
+<td><code>hdpi</code>,<br /><code>tvdpi</code></td>
+<td>Android 4.0+</td>
+<td>WXGA800-7in</td>
+</tr>
+<tr>
+<td><span style="white-space:nowrap">10-inch</span> tablet</td>
+<td><span style="white-space:nowrap"><code>xlarge</code> or</span><br /><code>-sw800</code></td>
+<td><code>mdpi</code>,<br /><code>hdpi</code></td>
+<td>Android 3.2+</td>
+<td>WXGA800</td>
+</tr>
+</table>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/spotlight/index.jd b/docs/html/distribute/googleplay/spotlight/index.jd
new file mode 100644
index 0000000..6acd914
--- /dev/null
+++ b/docs/html/distribute/googleplay/spotlight/index.jd
@@ -0,0 +1,46 @@
+page.title=Spotlight
+walkthru=0
+header.hide=0
+
+@jd:body
+
+
+<p>Android developers, their apps, and their successes with Android and Google Play. </p>
+
+
+
+<div style="background: #F0F0F0;
+ border-top: 1px solid #DDD;
+ padding: 0px 0 24px 0;
+ overflow: auto;
+ clear:both;
+ margin-bottom:-10px;
+ margin-top:30px;"">
+ <div style="padding:0 0 0 29px;">
+ <h4>Developer Story: Robot Invader</h4>
+ <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px height:78px;
+ width: 78px;
+ float: left;
+ margin: 17px 20px 9px 0;" src=
+ "//g0.gstatic.com/android/market/com.robotinvader.knightmare/hi-256-0-9e08d83bc8d01649e167131d197ada1cd1783fb0">
+ <div style="width:700px;">
+ <p style="margin-top:26px;margin-bottom:12px;">Robot Invader chose
+ Android and Google Play as the launch platform for their first game,<br />
+ <a data-g-event="Developers Page" data-g-label="Case Study Link" href=
+ "//play.google.com/store/apps/details?id=com.robotinvader.knightmare"><em>Wind-up
+ Knight</em></a>.
+ </p>
+ <p>
+ Hear from the developers how Android helped them reach millions of users
+ and more than 100 device models with a single app binary, then iterate rapidly to ensure
+ a great user experience.
+ </p>
+ </div>
+ <iframe style="float:left;
+ margin-right:24px;
+ margin-top:14px;" width="700" height="394" src=
+ "http://www.youtube.com/embed/hTtlLiUTowY" frameborder="0" allowfullscreen></iframe>
+ </div>
+</div>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/spotlight/tablets.jd b/docs/html/distribute/googleplay/spotlight/tablets.jd
new file mode 100644
index 0000000..641b794
--- /dev/null
+++ b/docs/html/distribute/googleplay/spotlight/tablets.jd
@@ -0,0 +1,283 @@
+page.title=Developer Stories: The Opportunity of Android Tablets
+walkthru=0
+header.hide=0
+
+@jd:body
+
+
+<p>More and more, developers are investing in a full tablet experience
+for their apps and are seeing those investments pay off big. The increased
+screen area on tablets opens up a world of possibilities, allowing for more
+engagement with the user — which can mean an increase in usage as well as
+more monetization opportunities. And with the growing wave of Android tablets that
+continue to hit the market, it’s an important piece of any developer’s mobile
+offering. </p>
+
+<p>Here are some stories from developers who are seeing real results as they
+expand their offering to include Android tablets.</p>
+
+
+<div style="margin-bottom:2em;"><!-- START STORY -->
+
+<h3>Mint: More screen real estate = more engagement</h3>
+
+ <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px height:78px;
+ width: 78px;
+ float: left;
+ margin: 12px 20px 9px 20px;" src=
+ "https://lh5.ggpht.com/0xAIZJ1uE05b4RHNHgBBTIH6nRdPTY660T104xY7O2GbHXwab6YVmpU5yYg8yacfBg=w124">
+
+ <div style="list-style: none;height:100%;
+ float: right;
+ border-top: 1px solid #9C0;
+ width: 220px;
+ margin: 4px 20px;padding: .5em;">
+
+
+ <h5>About the app</h5>
+
+
+ <ul>
+ <li><a href="http://play.google.com/store/apps/details?id=com.mint">Mint.com Personal Finance</a> by Intuit Inc.</li>
+ <li>Financial management app targeting 7- to 10-inch tablets</li>
+ </ul>
+
+ <h5>Tablet Results</h5>
+
+ <ul>
+ <li>Able to offer richer UI features</li>
+ <li>Much higher user engagement</li>
+ <li>Longer sessions — more Android tablet users have sessions longer than 5 minutes</li>
+ </ul>
+
+ <div style="padding:.5em 0 0 1em;">
+<a href="http://play.google.com/store/apps/details?id=com.mint">
+ <img alt="Android app on Google Play"
+ src="http://developer.android.com/images/brand/en_generic_rgb_wo_45.png" />
+</a>
+
+ </div>
+ </div>
+
+ <div style="line-height:1.4em;">
+ <p style="margin-top:0;margin-bottom:12px;">When Intuit was thinking about
+expanding their Mint mobile offering to include a version optimized for Android
+tablets, they knew that taking the layout that worked for phones and simply
+showing an enlarged version wouldn’t take full advantage of the opportunities
+that tablets afford.</p>
+
+ <p>“We knew we had a lot more real estate, and we wanted to provide a more
+immersive experience for our users” said Ken Sun, Intuit Group Product Manager
+at Mint.</p>
+
+<p>Intuit’s Mint app, which has a 4-star rating on Google Play, brings a number
+of features to Android tablets that aren’t available for phones, including a
+more visual presentation of personal financial data.</p>
+
+<p>“Whereas our app for phones is used throughout the day for quick sessions,
+we’ve seen a larger percentage of our tablet usage happen in the evening, for
+much longer sessions,” said Sun. “People are doing a lot more than just checking
+their spending. They’re looking at historical trends, re-categorizing
+transactions, analyzing the data and setting financial goals for the future
+— digging much deeper and being more thoughtful. One example is how much
+users are interacting with their own budgets in the tablet app. Customer budget
+operations (view, edit, drill-down, etc.) are 7x higher on Android tablets than
+they are on phones.”</p>
+
+<p>Fifty percent more Android tablet users have Mint sessions of 5 minutes or
+longer than they do on phones. “We’ve found that phone usage is indicative of a
+customer’s regular financial check-in, while tablet usage points towards more
+analysis and interaction with that customer’s personal financial data. This is
+the sort of immersive engagement experience we were looking for; the tablet and
+phone apps serve as great complements to each other."</p>
+ </div>
+
+ <div style="clear:both;margin-top:40px;width:auto;">
+
+ <a href=""><img src="{@docRoot}images/distribute/mint.png"></a>
+
+ <div style="width:600px;margin-top:0px;padding:0 90px;">
+ <p class="image-caption"><span style="font-weight:500;">Making the most of tablet screens</span>: Mint used the extra screen area on tablets to offer quick access to additional tools and information.</p>
+ </div>
+
+ </div>
+
+</div> <!-- END STORY -->
+
+
+<div style="margin:3em auto"><!-- START STORY -->
+
+
+<h3>TinyCo: Monetization opportunities abound on tablets</h3>
+
+ <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px height:78px;
+ width: 78px;
+ float: left;
+ margin: 12px 20px 9px 20px;" src=
+ "https://lh5.ggpht.com/mO1TPos65MWJF_n8ZrXMbNCqIqsvN4dQV_rwNOU3pF6N_Ii3lSiCPe_H_MP8C1MK5UKo=w124">
+
+
+ <div style="list-style: none;height:100%;
+ float: right;
+ border-top: 1px solid #9C0;
+ width: 220px;
+ margin: 4px 20px;padding: .5em;">
+
+ <h5>About the app</h5>
+
+ <ul>
+ <li><a href="http://play.google.com/store/apps/details?id=com.tinyco.village">Tiny Village</a> by TinyCo</li>
+ <li>Game targeting 7- to 10-inch tablets and phones</li>
+ </ul>
+
+ <h5>Tablet Results</h5>
+
+ <ul>
+ <li>35% higher average revenue per paying user (ARPPU)</li>
+ <li>Consistent increase in user retention</li>
+ <li>3x increase in downloads to Android tablets in the last 6 months</li>
+ </ul>
+
+ <div style="padding:.5em 0 0 1em;">
+<a href="http://play.google.com/store/apps/details?id=com.tinyco.village">
+ <img alt="Android app on Google Play"
+ src="http://developer.android.com/images/brand/en_generic_rgb_wo_45.png" />
+</a>
+
+ </div>
+ </div>
+
+ <div style="line-height:1.4em;">
+ <p style="margin-top:0;margin-bottom:12px;">Over a year ago, developer
+TinyCo, makers of games such as Tiny Monsters, switched to a
+simultaneous launch strategy for their products. They chose Android as one of their
+primary launch platforms because of its large installed base and global reach. They
+also knew that the growing base of Android tablet users represented a huge
+opportunity. </p>
+
+ <p>Tiny Village was their first title to take advantage of the strategy, and
+it proved to be a winning one — especially in terms of Android
+tablets.</p>
+
+ <p> “With continued optimization of the gameplay experience and a genuine
+commitment to our Android offering through our Griffin engine, all of our
+metrics started to rise,” said Rajeev Nagpal, Head of Product at TinyCo. In
+fact, they’ve seen Android tablet downloads more than triple in the last six
+months.</p>
+
+ <p>One of the first things they noticed about usage of Tiny Village on
+tablets was an increase in average revenue per paying user (ARPPU)—about 35%
+higher than on smaller-screen devices such as phones. Additionally, average
+revenue per user ARPU is now about 35% higher as well. “The game is just much
+more immersive on tablet.”</p>
+
+ <p>In addition to an increase in monetization metrics, they’ve also seen a
+consistent increase in retention over other platforms. “These are really
+important metrics for games — if you can get users to both stay around
+longer and spend more while they’re there, you have a recipe for success.”</p>
+ </div>
+
+ <div style="clear:both;margin-top:40px;width:auto;">
+
+ <a href=""><img src="{@docRoot}images/distribute/tinyvillage.png"></a>
+
+ <div style="width:600px;margin-top:0px;padding:0 90px;">
+ <p class="image-caption"><span style="font-weight:500;">More monetization
+on tablets</span>: On Android tablets TinyCo has seen higher ARPPU and user
+retention than on phones.</p>
+ </div>
+
+ </div>
+
+</div> <!-- END STORY -->
+
+
+<div style="margin-bottom:2em;"><!-- START STORY -->
+
+<h3>Instapaper: Riding the growing wave of Android tablets</h3>
+
+
+ <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px height:78px;
+ width: 78px;
+ float: left;
+ margin: 12px 20px 9px 20px;" src=
+ "https://lh3.ggpht.com/30KKcrIFO8V_wRfhnHaI9l0CLH_orIVFE7Xywtr9TBxAf0hi2BaZkKyBOs63Yfavpg=w124">
+
+
+ <div style="list-style: none;height:100%;
+ float: right;
+ border-top: 1px solid #9C0;
+ width: 220px;
+ margin: 4px 20px;padding: .5em;">
+
+
+
+
+ <h5>About the app</h5>
+ <ul>
+ <li><a href="http://play.google.com/store/apps/details?id=com.instapaper.android">Instapaper</a> by Mobelux</li>
+ <li>Content-browsing utility that targets 7- to 10-inch tablets and phones</li>
+ </ul>
+
+ <h5>Tablet Results</h5>
+
+ <ul>
+ <li>Tablets are now 50% of the app's installed base.</li>
+ </ul>
+
+ <div style="padding:.5em 0 0 1em;">
+<a href="http://play.google.com/store/apps/details?id=com.instapaper.android">
+ <img alt="Android app on Google Play"
+ src="http://developer.android.com/images/brand/en_generic_rgb_wo_45.png" />
+</a>
+
+ </div>
+ </div>
+
+ <div style="line-height:1.4em;">
+ <p style="margin-top:0;margin-bottom:12px;">Instapaper for Android is an app
+for saving web content to read later. Developer Mobelux decided that creating a
+great UI for Android tablet users would be an essential part of their initial launch
+plan.</p>
+
+ <p>The app launched at the beginning of the summer of 2012, just in time to
+take advantage of a new tide of Android tablets, including the <span
+style="white-space:nowrap;">Nexus 7</span> tablet. The app has since seen huge
+popularity among tablet users, in particular, on Nexus 7. On the day that
+pre-orders of Nexus 7 began arriving, Mobelux saw a 600% jump in downloads of
+its app on Google Play.</p>
+
+ <p>“We saw a promising new set of Android tablets about to hit the market
+and wanted to position ourselves to be ready for them” said Jeff Rock of
+Mobelux. “It was a market that others were hesitant to explore, but the decision
+to prioritize tablets has paid off very well for us.”</p>
+
+ <p>Since that initial 600% jump in downloads, Instapaper for Android has
+continued to see a successful run on Android tablets. In fact, Android tablets
+now represent about 50% of their installed base. “With more and more Android
+tablets coming online, we’re excited to see how our investment in Android
+tablets continues to pay off.”</p>
+ </div>
+
+ <div style="clear:both;margin-top:40px;width:auto;">
+
+ <a href=""><img src="{@docRoot}images/distribute/instapaper.png"></a>
+
+ <div style="width:600px;margin-top:0px;padding:0 90px;">
+ <p class="image-caption"><span style="font-weight:500;">Popular with
+tablet users</span>: A great tablet UI and browsing convenience make Instapaper
+popular with Android tablet users.</p>
+ </div>
+
+ </div>
+
+</div> <!-- END STORY -->
+
+
+
diff --git a/docs/html/distribute/googleplay/strategies/app-quality.jd b/docs/html/distribute/googleplay/strategies/app-quality.jd
index 6ea862b..ecc51dc 100644
--- a/docs/html/distribute/googleplay/strategies/app-quality.jd
+++ b/docs/html/distribute/googleplay/strategies/app-quality.jd
@@ -1,10 +1,10 @@
-page.title=Improving App Quality
+page.title=Improving App Quality After Launch
@jd:body
<div id="qv-wrapper">
<div id="qv">
-<h2>Strategies:</h2>
-<ul>
+<h2>Strategies</h2>
+<ol>
<li><a href="#listen">Listen to Your Users</a></li>
<li><a href="#stability">Improve Stability and Eliminate Bugs</a></li>
<li><a href="#responsiveness">Improve UI Responsiveness</a></li>
@@ -13,7 +13,14 @@
<li><a href="#features">Deliver the Right Set of Features</a></li>
<li><a href="#integrate">Integrate with the System and Third-Party Apps</a></li>
<li><a href="#details">Pay Attention to Details</a></li>
-</ul>
+</ol>
+
+<h2>You Should Also Read</h2>
+<ol>
+<li><a href="{@docRoot}distribute/googleplay/quality/core.html">Core App Quality Guidelines</a></li>
+<li><a href="{@docRoot}distribute/googleplay/quality/tablet.html">Tablet App Quality Checklist</a></li>
+</ol>
+
</div>
</div>
@@ -22,7 +29,7 @@
<p>
A better app can go a very long way: a higher quality app will translate to higher user ratings, generally better rankings, more downloads, and higher retention (longer install periods). High-quality apps also have a much higher likelihood of getting some unanticipated positive publicity such as being featured in Google Play or getting social media buzz.</p>
<p>
-The upside to having a higher-quality app is obvious. However, it's not always clear how to make an app "better". The path to improving app quality isn't always well-lit. The term "quality" — along with "polish" and "fit and finish" — aren't always well-defined. Here we'll light the path by looking at some of the key factors in app quality and ways of improving your app along these dimensions.</p>
+The upside to having a higher-quality app is obvious. However, it's not always clear how to make an app "better". This document looks at some of the key factors in app quality and ways of improving your app over time, after you've launched the app.</p>
<h2 id="listen">Listen to Your Users</h2>
<p>
@@ -52,15 +59,14 @@
<p>
You can improve your apps's UI responsiveness by moving long-running operations off the main thread to worker threads. Android offers built-in debugging facilities such as StrictMode for analyzing your app's performance and activities on the main thread. You can see more recommendations in <a href="http://www.youtube.com/watch?v=c4znvD-7VDA">Writing Zippy Android Apps</a>, a developer session from Google I/O 2010,</p>
-
<div class="sidebox-wrapper">
<div class="sidebox">
-<h2>More resources</h2>
+<h3>More resources</h3>
<ul>
<li><a href="{@docRoot}design/index.html">Android Design</a></li>
<li><a href="{@docRoot}guide/practices/performance.html">Designing for Performance</a></li>
<li><a href="{@docRoot}guide/practices/responsiveness.html">Designing for Responsiveness</a>
-<li><a href="{@docRoot}guide/practices/seamlessness.html">Designing for seamlessness</a>
+<li><a href="{@docRoot}guide/practices/seamlessness.html">Designing for Seamlessness</a>
</li>
</ul>
</div></div>
@@ -73,18 +79,17 @@
<h2 id="usability">Improve Usability</h2>
<p>
In usability and in app design too, you should listen carefully to your users. Ask a handful of real Android device users (friends, family, etc.) to try out your app and observe them as they interact with it. Look for cases where they get confused, are unsure of how to proceed, or are surprised by certain behaviors. Minimize these cases by rethinking some of the interactions in your app, perhaps working in some of the <a href="http://www.youtube.com/watch?v=M1ZBjlCRfz0">user interface patterns</a> the Android UI team discussed at Google I/O.</p>
-<p>
-In the same vein, two problems that can plague some Android user interfaces are small tap targets and excessively small font sizes. These are generally easy to fix and can make a big impact on usability and user satisfaction. As a general rule, optimize for ease of use and legibility, while minimizing, or at least carefully balancing, information density.</p>
<div class="sidebox-wrapper">
<div class="sidebox">
-<h2>More resources</h2>
-<ul>
-As you are designing or evaluating your app's UI, make sure to read and become familiar with the <a href="{@docRoot}design/index.html">Android Design</a> guidelines. Included are many examples of UI patterns, styles, and building blocks, as well as tools for the design process.</li>
-</ul>
+<p>
+As you are designing or evaluating your app's UI, make sure to read and become familiar with the <a href="/design/index.html">Android Design</a> guidelines. Included are many examples of UI patterns, styles, and building blocks, as well as tools for the design process.</p>
</div></div>
<p>
+In the same vein, two problems that can plague some Android user interfaces are small tap targets and excessively small font sizes. These are generally easy to fix and can make a big impact on usability and user satisfaction. As a general rule, optimize for ease of use and legibility, while minimizing, or at least carefully balancing, information density.</p>
+
+<p>
Another way to incrementally improve usability, based on real-world data, is to implement <a href="http://code.google.com/mobile/analytics/docs/">Analytics</a> throughout your app to log usage of particular sections. Consider demoting infrequently used sections to the overflow menu in the <a href="{@docRoot}design/patterns/actionbar.html">Action bar</a>, or removing them altogether. For often-used sections and UI elements, make sure they're immediately obvious and easily accessible in your app's UI so that users can get to them quickly.</p>
<p>
Lastly, usability is an extensive and well-documented subject, with close ties to interface design, cognitive science, and other disciplines.</p>
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index d1c24df..62c054a 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -1,680 +1,806 @@
page.title=Dialogs
-parent.title=User Interface
-parent.link=index.html
@jd:body
+
+
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
+<ol>
+ <li><a href="#DialogFragment">Creating a Dialog Fragment</a></li>
+ <li><a href="#AlertDialog">Building an Alert Dialog</a>
<ol>
- <li><a href="#ShowingADialog">Showing a Dialog</a></li>
- <li><a href="#DismissingADialog">Dismissing a Dialog</a></li>
- <li><a href="#AlertDialog">Creating an AlertDialog</a>
- <ol>
- <li><a href="#AddingButtons">Adding buttons</a></li>
- <li><a href="#AddingAList">Adding a list</a></li>
- </ol>
- </li>
- <li><a href="#ProgressDialog">Creating a ProgressDialog</a>
- <ol>
- <li><a href="#ShowingAProgressBar">Showing a progress bar</a></li>
- </ol>
- </li>
- <li><a href="#CustomDialog">Creating a Custom Dialog</a></li>
+ <li><a href="#AddingButtons">Adding buttons</a></li>
+ <li><a href="#AddingAList">Adding a list</a></li>
+ <li><a href="#CustomLayout">Creating a Custom Layout</a></li>
</ol>
+ </li>
+ <li><a href="#PassingEvents">Passing Events Back to the Dialog's Host</a></li>
+ <li><a href="#ShowingADialog">Showing a Dialog</a></li>
+ <li><a href="#FullscreenDialog">Showing a Dialog Fullscreen or as an Embedded Fragment</a>
+ <ol>
+ <li><a href="#ActivityAsDialog">Showing an activity as a dialog on large screens</a></li>
+ </ol>
+ </li>
+ <li><a href="#DismissingADialog">Dismissing a Dialog</a></li>
+</ol>
<h2>Key classes</h2>
<ol>
- <li>{@link android.app.Dialog}</li>
- <li>{@link android.app.AlertDialog}</li>
<li>{@link android.app.DialogFragment}</li>
- </ol>
-
- <h2>Related tutorials</h2>
- <ol>
- <li><a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Hello
-DatePicker</a></li>
- <li><a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Hello
-TimePicker</a></li>
+ <li>{@link android.app.AlertDialog}</li>
</ol>
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}design/building-blocks/dialogs.html">Android Design: Dialogs</a></li>
+ <li><a href="{@docRoot}design/building-blocks/dialogs.html">Dialogs design guide</a></li>
+ <li><a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> (Date/Time dialogs)</li>
</ol>
</div>
</div>
-<p>A dialog is usually a small window that appears in front of the current Activity.
-The underlying Activity loses focus and the dialog accepts all user interaction. Dialogs are
-normally used for notifications that should interrupt the user and to perform short tasks that
-directly relate to the application in progress (such as a progress bar or a login prompt).</p>
-
-<p>The {@link android.app.Dialog} class is the base class for creating dialogs. However, you
-typically should not instantiate a {@link android.app.Dialog} directly. Instead, you should use one
-of the following subclasses:</p>
-<dl>
- <dt>{@link android.app.AlertDialog}</dt>
- <dd>A dialog that can manage zero, one, two, or three buttons, and/or a list of
- selectable items that can include checkboxes or radio buttons. The AlertDialog
- is capable of constructing most dialog user interfaces and is the suggested dialog type.
- See <a href="#AlertDialog">Creating an AlertDialog</a> below.</dd>
- <dt>{@link android.app.ProgressDialog}</dt>
- <dd>A dialog that displays a progress wheel or progress bar. Because it's an extension of
- the AlertDialog, it also supports buttons.
- See <a href="#ProgressDialog">Creating a ProgressDialog</a> below.</dd>
- <dt>{@link android.app.DatePickerDialog}</dt>
- <dd>A dialog that allows the user to select a date. See the
- <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Hello DatePicker</a> tutorial.</dd>
- <dt>{@link android.app.TimePickerDialog}</dt>
- <dd>A dialog that allows the user to select a time. See the
- <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Hello TimePicker</a> tutorial.</dd>
-</dl>
-
-<p>If you would like to customize your own dialog, you can extend the
-base {@link android.app.Dialog} object or any of the subclasses listed above and define a new layout.
-See the section on <a href="#CustomDialog">Creating a Custom Dialog</a> below.</p>
+<p>A dialog is a small window that prompts the user to
+make a decision or enter additional information. A dialog does not fill the screen and is
+normally used for modal events that require users to take an action before they can proceed.</p>
<div class="note design">
<p><strong>Dialog Design</strong></p>
- <p>For design guidelines, read Android Design's <a
-href="{@docRoot}design/building-blocks/dialogs.html">Dialogs</a> guide.</p>
+ <p>For information about how to design your dialogs, including recommendations
+ for language, read the <a
+href="{@docRoot}design/building-blocks/dialogs.html">Dialogs</a> design guide.</p>
</div>
+<img src="{@docRoot}images/ui/dialogs.png" />
+
+<p>The {@link android.app.Dialog} class is the base class for dialogs, but you
+should avoid instantiating {@link android.app.Dialog} directly.
+Instead, use one of the following subclasses:</p>
+<dl>
+ <dt>{@link android.app.AlertDialog}</dt>
+ <dd>A dialog that can show a title, up to three buttons, a list of
+ selectable items, or a custom layout.</dd>
+ <dt>{@link android.app.DatePickerDialog} or {@link android.app.TimePickerDialog}</dt>
+ <dd>A dialog with a pre-defined UI that allows the user to select a date or time.</dd>
+</dl>
+
+<div class="sidebox">
+<h2>Avoid ProgressDialog</h2>
+<p>Android includes another dialog class called
+{@link android.app.ProgressDialog} that shows a dialog with a progress bar. However, if you
+need to indicate loading or indeterminate progress, you should instead follow the design
+guidelines for <a href="{@docRoot}design/building-blocks/progress.html">Progress &
+Activity</a> and use a {@link android.widget.ProgressBar} in your layout.</p>
+</div>
+
+<p>These classes define the style and structure for your dialog, but you should
+use a {@link android.support.v4.app.DialogFragment} as a container for your dialog.
+The {@link android.support.v4.app.DialogFragment} class provides all the controls you
+need to create your dialog and manage its appearance, instead of calling methods
+on the {@link android.app.Dialog} object.</p>
+
+<p>Using {@link android.support.v4.app.DialogFragment} to manage the dialog
+ensures that it correctly handles lifecycle events
+such as when the user presses the <em>Back</em> button or rotates the screen. The {@link
+android.support.v4.app.DialogFragment} class also allows you to reuse the dialog's UI as an
+embeddable component in a larger UI, just like a traditional {@link
+android.support.v4.app.Fragment} (such as when you want the dialog UI to appear differently
+on large and small screens).</p>
+
+<p>The following sections in this guide describe how to use a {@link
+android.support.v4.app.DialogFragment} in combination with an {@link android.app.AlertDialog}
+object. If you'd like to create a date or time picker, you should instead read the
+<a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> guide.</p>
+
+<p class="note"><strong>Note:</strong>
+Because the {@link android.app.DialogFragment} class was originally added with
+Android 3.0 (API level 11), this document describes how to use the {@link
+android.support.v4.app.DialogFragment} class that's provided with the <a
+href="{@docRoot}tools/extras/support-library.html">Support Library</a>. By adding this library
+to your app, you can use {@link android.support.v4.app.DialogFragment} and a variety of other
+APIs on devices running Android 1.6 or higher. If the minimum version your app supports
+is API level 11 or higher, then you can use the framework version of {@link
+android.app.DialogFragment}, but be aware that the links in this document are for the support
+library APIs. When using the support library,
+be sure that you import <code>android.support.v4.app.DialogFragment</code>
+class and <em>not</em> <code>android.app.DialogFragment</code>.</p>
+
+
+<h2 id="DialogFragment">Creating a Dialog Fragment</h2>
+
+<p>You can accomplish a wide variety of dialog designs—including
+custom layouts and those described in the <a
+href="{@docRoot}design/building-blocks/dialogs.html">Dialogs</a>
+design guide—by extending
+{@link android.support.v4.app.DialogFragment} and creating a {@link android.app.AlertDialog}
+in the {@link android.support.v4.app.DialogFragment#onCreateDialog
+onCreateDialog()} callback method.</p>
+
+<p>For example, here's a basic {@link android.app.AlertDialog} that's managed within
+a {@link android.support.v4.app.DialogFragment}:</p>
+
+<pre>
+public class FireMissilesDialog extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Use the Builder class for convenient dialog construction
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.dialog_fire_missiles)
+ .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // FIRE ZE MISSILES!
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // User cancelled the dialog
+ }
+ });
+ // Create the AlertDialog object and return it
+ return builder.create();
+ }
+}
+</pre>
+
+<div class="figure" style="width:290px;margin:0 0 0 20px">
+<img src="{@docRoot}images/ui/dialog_buttons.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong>
+A dialog with a message and two action buttons.</p>
+</div>
+
+<p>Now, when you create an instance of this class and call {@link
+android.support.v4.app.DialogFragment#show show()} on that object, the dialog appears as
+shown in figure 1.</p>
+
+<p>The next section describes more about using the {@link android.app.AlertDialog.Builder}
+APIs to create the dialog.</p>
+
+<p>Depending on how complex your dialog is, you can implement a variety of other callback
+methods in the {@link android.support.v4.app.DialogFragment}, including all the basic
+<a href="{@docRoot}guide/components/fragments.html#Lifecycle">fragment lifecycle methods</a>.
+
+
+
+
+
+<h2 id="AlertDialog">Building an Alert Dialog</h2>
+
+
+<p>The {@link android.app.AlertDialog} class allows you to build a variety of dialog designs and
+is often the only dialog class you'll need.
+As shown in figure 2, there are three regions of an alert dialog:</p>
+
+<div class="figure" style="width:311px;margin-top:0">
+<img src="{@docRoot}images/ui/dialogs_regions.png" alt="" style="margin-bottom:0"/>
+<p class="img-caption"><strong>Figure 2.</strong> The layout of a dialog.</p>
+</div>
+
+<ol>
+<li><b>Title</b>
+ <p>This is optional and should be used only when the content area
+ is occupied by a detailed message, a list, or custom layout. If you need to state
+ a simple message or question (such as the dialog in figure 1), you don't need a title.</li>
+<li><b>Content area</b>
+ <p>This can display a message, a list, or other custom layout.</p></li>
+<li><b>Action buttons</b>
+ <p>There should be no more than three action buttons in a dialog.</p></li>
+</ol>
+
+<p>The {@link android.app.AlertDialog.Builder}
+class provides APIs that allow you to create an {@link android.app.AlertDialog}
+with these kinds of content, including a custom layout.</p>
+
+<p>To build an {@link android.app.AlertDialog}:</p>
+
+<pre>
+<b>// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor</b>
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+<b>// 2. Chain together various setter methods to set the dialog characteristics</b>
+builder.setMessage(R.string.dialog_message)
+ .setTitle(R.string.dialog_title);
+
+<b>// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}</b>
+AlertDialog dialog = builder.create();
+</pre>
+
+<p>The following topics show how to define various dialog attributes using the
+{@link android.app.AlertDialog.Builder} class.</p>
+
+
+
+
+<h3 id="AddingButtons">Adding buttons</h3>
+
+<p>To add action buttons like those in figure 2,
+call the {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} and
+{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()} methods:</p>
+
+<pre style="clear:right">
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+// Add the buttons
+builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // User clicked OK button
+ }
+ });
+builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // User cancelled the dialog
+ }
+ });
+// Set other dialog properties
+...
+
+// Create the AlertDialog
+AlertDialog dialog = builder.create();
+</pre>
+
+<p>The <code>set...Button()</code> methods require a title for the button (supplied
+by a <a href="{@docRoot}guide/topics/resources/string-resource.html">string resource</a>) and a
+{@link android.content.DialogInterface.OnClickListener} that defines the action to take
+when the user presses the button.</p>
+
+<p>There are three different action buttons you can add:</p>
+<dl>
+ <dt>Positive</dt>
+ <dd>You should use this to accept and continue with the action (the "OK" action).</dd>
+ <dt>Negative</dt>
+ <dd>You should use this to cancel the action.</dd>
+ <dt>Neutral</dt>
+ <dd>You should use this when the user may not want to proceed with the action,
+ but doesn't necessarily want to cancel. It appears between the positive and negative
+ buttons. For example, the action might be "Remind me later."</dd>
+</dl>
+
+<p>You can add only one of each button type to an {@link
+android.app.AlertDialog}. That is, you cannot have more than one "positive" button.</p>
+
+
+
+<div class="figure" style="width:290px;margin:0 0 0 40px">
+<img src="{@docRoot}images/ui/dialog_list.png" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong>
+A dialog with a title and list.</p>
+</div>
+
+<h3 id="AddingAList">Adding a list</h3>
+
+<p>There are three kinds of lists available with the {@link android.app.AlertDialog} APIs:</p>
+<ul>
+<li>A traditional single-choice list</li>
+<li>A persistent single-choice list (radio buttons)</li>
+<li>A persistent multiple-choice list (checkboxes)</li>
+</ul>
+
+<p>To create a single-choice list like the one in figure 3,
+use the {@link android.app.AlertDialog.Builder#setItems setItems()} method:</p>
+
+<pre style="clear:right">
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.pick_color);
+ .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // The 'which' argument contains the index position
+ // of the selected item
+ }
+ });
+ return builder.create();
+}
+</pre>
+
+<p>Because the list appears in the dialog's content area,
+the dialog cannot show both a message and a list and you should set a title for the
+dialog with {@link android.app.AlertDialog.Builder#setTitle setTitle()}.
+To specify the items for the list, call {@link
+android.app.AlertDialog.Builder#setItems setItems()}, passing an array.
+Alternatively, you can specify a list using {@link
+android.app.AlertDialog.Builder#setAdapter setAdapter()}. This allows you to back the list
+with dynamic data (such as from a database) using a {@link android.widget.ListAdapter}.</p>
+
+<p>If you choose to back your list with a {@link android.widget.ListAdapter},
+always use a {@link android.support.v4.content.Loader} so that the content loads
+asynchronously. This is described further in
+<a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">Building Layouts
+with an Adapter</a> and the <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+guide.</p>
+
+<p class="note"><strong>Note:</strong> By default, touching a list item dismisses the dialog,
+unless you're using one of the following persistent choice lists.</p>
+
+<div class="figure" style="width:290px;margin:-30px 0 0 40px">
+<img src="{@docRoot}images/ui/dialog_checkboxes.png" />
+<p class="img-caption"><strong>Figure 4.</strong>
+A list of multiple-choice items.</p>
+</div>
+
+
+<h4 id="Checkboxes">Adding a persistent multiple-choice or single-choice list</h4>
+
+<p>To add a list of multiple-choice items (checkboxes) or
+single-choice items (radio buttons), use the
+{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String,
+DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} or
+{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener)
+setSingleChoiceItems()} methods, respectively.</p>
+
+<p>For example, here's how you can create a multiple-choice list like the
+one shown in figure 4 that saves the selected
+items in an {@link java.util.ArrayList}:</p>
+
+<pre style="clear:right">
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mSelectedItems = new ArrayList(); // Where we track the selected items
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ // Set the dialog title
+ builder.setTitle(R.string.pick_toppings)
+ // Specify the list array, the items to be selected by default (null for none),
+ // and the listener through which to receive callbacks when items are selected
+ .setMultiChoiceItems(R.array.toppings, null,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which,
+ boolean isChecked) {
+ if (isChecked) {
+ // If the user checked the item, add it to the selected items
+ mSelectedItems.add(which);
+ } else if (mSelectedItems.contains(which)) {
+ // Else, if the item is already in the array, remove it
+ mSelectedItems.remove(Integer.valueOf(which));
+ }
+ }
+ })
+ // Set the action buttons
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ // User clicked OK, so save the mSelectedItems results somewhere
+ // or return them to the component that opened the dialog
+ ...
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ ...
+ }
+ });
+
+ return builder.create();
+}
+</pre>
+
+<p>Although both a traditional list and a list with radio buttons
+provide a "single choice" action, you should use {@link
+android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener)
+setSingleChoiceItems()} if you want to persist the user's choice.
+That is, if opening the dialog again later should indicate what the user's current choice is,
+then you create a list with radio buttons.</p>
+
+
+
+
+
+<h3 id="CustomLayout">Creating a Custom Layout</h3>
+
+<div class="figure" style="width:290px;margin:-30px 0 0 40px">
+<img src="{@docRoot}images/ui/dialog_custom.png" alt="" />
+<p class="img-caption"><strong>Figure 5.</strong> A custom dialog layout.</p>
+</div>
+
+<p>If you want a custom layout in a dialog, create a layout and add it to an
+{@link android.app.AlertDialog} by calling {@link
+android.app.AlertDialog.Builder#setView setView()} on your {@link
+android.app.AlertDialog.Builder} object.</p>
+
+<p>By default, the custom layout fills the dialog window, but you can still
+use {@link android.app.AlertDialog.Builder} methods to add buttons and a title.</p>
+
+<p>For example, here's the layout file for the dialog in Figure 5:</p>
+
+<p style="clear:right" class="code-caption">res/layout/dialog_signin.xml</p>
+<pre>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:src="@drawable/header_logo"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:scaleType="center"
+ android:background="#FFFFBB33"
+ android:contentDescription="@string/app_name" />
+ <EditText
+ android:id="@+id/username"
+ android:inputType="textEmailAddress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginBottom="4dp"
+ android:hint="@string/username" />
+ <EditText
+ android:id="@+id/password"
+ android:inputType="textPassword"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginBottom="16dp"
+ android:fontFamily="sans-serif"
+ android:hint="@string/password"/>
+</LinearLayout>
+</pre>
+
+<p class="note"><strong>Tip:</strong> By default, when you set an {@link android.widget.EditText}
+element to use the {@code "textPassword"} input type, the font family is set to monospace, so
+you should change its font family to {@code "sans-serif"} so that both text fields use
+a matching font style.</p>
+
+<p>To inflate the layout in your {@link android.support.v4.app.DialogFragment},
+get a {@link android.view.LayoutInflater} with
+{@link android.app.Activity#getLayoutInflater()} and call
+{@link android.view.LayoutInflater#inflate inflate()}, where the first parameter
+is the layout resource ID and the second parameter is a parent view for the layout.
+You can then call {@link android.app.AlertDialog#setView setView()}
+to place the layout in the dialog.</p>
+
+<pre>
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ // Get the layout inflater
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ // Inflate and set the layout for the dialog
+ // Pass null as the parent view because its going in the dialog layout
+ builder.setView(inflater.inflate(R.layout.dialog_signin, null))
+ // Add action buttons
+ .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ // sign in the user ...
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ NoticeDialog.this.getDialog().cancel();
+ }
+ });
+ return builder.create();
+}
+</pre>
+
+<div class="note">
+<p><strong>Tip:</strong> If you want a custom dialog,
+you can instead display an {@link android.app.Activity} as a dialog
+instead of using the {@link android.app.Dialog} APIs. Simply create an activity and set its theme to
+{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog}
+in the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
+<activity>}</a> manifest element:</p>
+
+<pre>
+<activity android:theme="@android:style/Theme.Holo.Dialog" >
+</pre>
+<p>That's it. The activity now displays in a dialog window instead of fullscreen.</p>
+</div>
+
+
+
+<h2 id="PassingEvents">Passing Events Back to the Dialog's Host</h2>
+
+<p>When the user touches one of the dialog's action buttons or selects an item from its list,
+your {@link android.support.v4.app.DialogFragment} might perform the necessary
+action itself, but often you'll want to deliver the event to the activity or fragment that
+opened the dialog. To do this, define an interface with a method for each type of click event,
+then implement that interface in the host component that will
+receive the action events from the dialog.</p>
+
+<p>For example, here's a {@link android.support.v4.app.DialogFragment} that defines an
+interface through which it delivers the events back to the host activity:</p>
+
+<pre>
+public class NoticeDialog extends DialogFragment {
+
+ /* The activity that creates an instance of this dialog fragment must
+ * implement this interface in order to receive event callbacks.
+ * Each method passes the DialogFragment in case the host needs to query it. */
+ public interface NoticeDialogListener {
+ public void onDialogPositiveClick(DialogFragment dialog);
+ public void onDialogNegativeClick(DialogFragment dialog);
+ }
+
+ // Use this instance of the interface to deliver action events
+ static NoticeDialogListener mListener;
+
+ /* Call this to instantiate a new NoticeDialog.
+ * @param activity The activity hosting the dialog, which must implement the
+ * NoticeDialogListener to receive event callbacks.
+ * @returns A new instance of NoticeDialog.
+ * @throws ClassCastException if the host activity does not
+ * implement NoticeDialogListener
+ */
+ public static NoticeDialog newInstance(Activity activity) {
+ // Verify that the host activity implements the callback interface
+ try {
+ // Instantiate the NoticeDialogListener so we can send events with it
+ mListener = (NoticeDialogListener) activity;
+ } catch (ClassCastException e) {
+ // The activity doesn't implement the interface, throw exception
+ throw new ClassCastException(activity.toString()
+ + " must implement NoticeDialogListener");
+ }
+ NoticeDialog frag = new NoticeDialog();
+ return frag;
+ }
+
+ ...
+}
+</pre>
+
+<p>The activity hosting the dialog creates and shows an instance of the dialog
+by calling {@code NoticeDialog.newInstance()} and receives the dialog's
+events through an implementation of the {@code NoticeDialogListener} interface:</p>
+
+<pre>
+public class MainActivity extends FragmentActivity
+ implements NoticeDialog.NoticeDialogListener{
+ ...
+
+ public void showNoticeDialog() {
+ // Create an instance of the dialog fragment and show it
+ DialogFragment dialog = NoticeDialog.newInstance(this);
+ dialog.show(getSupportFragmentManager(), "NoticeDialog");
+ }
+
+ @Override
+ public void onDialogPositiveClick(DialogFragment dialog) {
+ // User touched the dialog's positive button
+ ...
+ }
+
+ @Override
+ public void onDialogNegativeClick(DialogFragment dialog) {
+ // User touched the dialog's negative button
+ ...
+ }
+}
+</pre>
+
+<p>Because the host activity implements the {@code NoticeDialogListener}—which is
+enforced by the {@code newInstance()} method shown above—the dialog fragment can use the
+interface callback methods to deliver click events to the activity:</p>
+
+<pre>
+public class NoticeDialog extends DialogFragment {
+ ...
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Build the dialog and set up the button click handlers
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.dialog_fire_missiles)
+ .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // Send the positive button event back to the host activity
+ mListener.onDialogPositiveClick(NoticeDialog.this);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // Send the negative button event back to the host activity
+ mListener.onDialogPositiveClick(NoticeDialog.this);
+ }
+ });
+ return builder.create();
+ }
+}
+</pre>
+
+
+
<h2 id="ShowingADialog">Showing a Dialog</h2>
-<p>A dialog is always created and displayed as a part of an {@link android.app.Activity}.
-You should normally create dialogs from within your Activity's
-{@link android.app.Activity#onCreateDialog(int)} callback method.
-When you use this callback, the Android system automatically manages the state of
-each dialog and hooks them to the Activity, effectively making it the "owner" of each dialog.
-As such, each dialog inherits certain properties from the Activity. For example, when a dialog
-is open, the Menu key reveals the options menu defined for the Activity and the volume
-keys modify the audio stream used by the Activity.</p>
+<p>When you want to show your dialog, create an instance of your {@link
+android.support.v4.app.DialogFragment} and call {@link android.support.v4.app.DialogFragment#show
+show()}, passing the {@link android.support.v4.app.FragmentManager} and a tag name
+for the dialog fragment.</p>
-<p class="note"><strong>Note:</strong> If you decide to create a dialog outside of the
-<code>onCreateDialog()</code> method, it will not be attached to an Activity. You can, however,
-attach it to an Activity with {@link android.app.Dialog#setOwnerActivity(Activity)}.</p>
+<p>You can get the {@link android.support.v4.app.FragmentManager} by calling
+{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} from
+the {@link android.support.v4.app.FragmentActivity} or {@link
+android.support.v4.app.Fragment#getFragmentManager()} from a {@link
+android.support.v4.app.Fragment}. For example:</p>
-<p>When you want to show a dialog, call
-{@link android.app.Activity#showDialog(int)} and pass it an integer that uniquely identifies the
-dialog that you want to display.</p>
-
-<p>When a dialog is requested for the first time, Android calls
-{@link android.app.Activity#onCreateDialog(int)} from your Activity, which is
-where you should instantiate the {@link android.app.Dialog}. This callback method
-is passed the same ID that you passed to {@link android.app.Activity#showDialog(int)}.
-After you create the Dialog, return the object at the end of the method.</p>
-
-<p>Before the dialog is displayed, Android also calls the optional callback method
-{@link android.app.Activity#onPrepareDialog(int,Dialog)}. Define this method if you want to change
-any properties of the dialog each time it is opened. This method is called
-every time a dialog is opened, whereas {@link android.app.Activity#onCreateDialog(int)} is only
-called the very first time a dialog is opened. If you don't define
-{@link android.app.Activity#onPrepareDialog(int,Dialog) onPrepareDialog()}, then the dialog will
-remain the same as it was the previous time it was opened. This method is also passed the dialog's
-ID, along with the Dialog object you created in {@link android.app.Activity#onCreateDialog(int)
-onCreateDialog()}.</p>
-
-<p>The best way to define the {@link android.app.Activity#onCreateDialog(int)} and
-{@link android.app.Activity#onPrepareDialog(int,Dialog)} callback methods is with a
-<em>switch</em> statement that checks the <var>id</var> parameter that's passed into the method.
-Each <em>case</em> should check for a unique dialog ID and then create and define the respective Dialog.
-For example, imagine a game that uses two different dialogs: one to indicate that the game
-has paused and another to indicate that the game is over. First, define an integer ID for
-each dialog:</p>
<pre>
-static final int DIALOG_PAUSED_ID = 0;
-static final int DIALOG_GAMEOVER_ID = 1;
-</pre>
-
-<p>Then, define the {@link android.app.Activity#onCreateDialog(int)} callback with a
-switch case for each ID:</p>
-<pre>
-protected Dialog onCreateDialog(int id) {
- Dialog dialog;
- switch(id) {
- case DIALOG_PAUSED_ID:
- // do the work to define the pause Dialog
- break;
- case DIALOG_GAMEOVER_ID:
- // do the work to define the game over Dialog
- break;
- default:
- dialog = null;
- }
- return dialog;
+public void confirmFireMissiles() {
+ DialogFragment newFragment = FireMissilesDialog.newInstance(this);
+ newFragment.show(getSupportFragmentManager(), "missiles");
}
</pre>
-<p class="note"><strong>Note:</strong> In this example, there's no code inside
-the case statements because the procedure for defining your Dialog is outside the scope
-of this section. See the section below about <a href="#AlertDialog">Creating an AlertDialog</a>,
-offers code suitable for this example.</p>
+<p>The second argument, {@code "missiles"}, is a unique tag name that the system uses to save
+and restore the fragment state when necessary. The tag also allows you to get a handle to
+the fragment by calling {@link android.support.v4.app.FragmentManager#findFragmentByTag
+findFragmentByTag()}.</p>
-<p>When it's time to show one of the dialogs, call {@link android.app.Activity#showDialog(int)}
-with the ID of a dialog:</p>
+
+
+
+<h2 id="FullscreenDialog">Showing a Dialog Fullscreen or as an Embedded Fragment</h2>
+
+<p>You might have a UI design in which you want a piece of the UI to appear as a dialog in some
+situations, but as a full screen or embedded fragment in others (perhaps depending on whether
+the device is a large screen or small screen). The {@link android.support.v4.app.DialogFragment}
+class offers you this flexibility because it can still behave as an embeddable {@link
+android.support.v4.app.Fragment}.</p>
+
+<p>However, you cannot use {@link android.app.AlertDialog.Builder AlertDialog.Builder}
+or other {@link android.app.Dialog} objects to build the dialog in this case. If
+you want the {@link android.support.v4.app.DialogFragment} to be
+embeddable, you must define the dialog's UI in a layout, then load the layout in the
+{@link android.support.v4.app.DialogFragment#onCreateView
+onCreateView()} callback.</p>
+
+<p>Here's an example {@link android.support.v4.app.DialogFragment} that can appear as either a
+dialog or an embeddable fragment (using a layout named <code>purchase_items.xml</code>):</p>
+
<pre>
-showDialog(DIALOG_PAUSED_ID);
+public class CustomLayoutDialog extends DialogFragment {
+ /** The system calls this to get the DialogFragment's layout, regardless
+ of whether it's being displayed as a dialog or an embedded fragment. */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout to use as dialog or embedded fragment
+ return inflater.inflate(R.layout.purchase_items, container, false);
+ }
+
+ /** The system calls this only when creating the layout in a dialog. */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // The only reason you might override this method when using onCreateView() is
+ // to modify any dialog characteristics. For example, the dialog includes a
+ // title by default, but your custom layout might not need it. So here you can
+ // remove the dialog title, but you must call the superclass to get the Dialog.
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ return dialog;
+ }
+}
</pre>
+<p>And here's some code that decides whether to show the fragment as a dialog
+or a fullscreen UI, based on the screen size:</p>
+
+<pre>
+public void showDialog() {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ CustomLayoutDialog newFragment = new CustomLayoutDialog();
+
+ if (mIsLargeLayout) {
+ // The device is using a large layout, so show the fragment as a dialog
+ newFragment.show(fragmentManager, "dialog");
+ } else {
+ // The device is smaller, so show the fragment fullscreen
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ // For a little polish, specify a transition animation
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ // To make it fullscreen, use the 'content' root view as the container
+ // for the fragment, which is always the root view for the activity
+ transaction.add(android.R.id.content, newFragment)
+ .addToBackStack(null).commit();
+ }
+}
+</pre>
+
+<p>For more information about performing fragment transactions, see the
+<a href="{@docRoot}guide/components/fragments.html">Fragments</a> guide.</p>
+
+<p>In this example, the <code>mIsLargeLayout</code> boolean specifies whether the current device
+should use the app's large layout design (and thus show this fragment as a dialog, rather
+than fullscreen). The best way to set this kind of boolean is to declare a
+<a href="{@docRoot}guide/topics/resources/more-resources.html#Bool">bool resource value</a>
+with an <a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources"
+>alternative resource</a> value for different screen sizes. For example, here are two
+versions of the bool resource for different screen sizes:</p>
+
+<p class="code-caption">res/values/bools.xml</p>
+<pre>
+<!-- Default boolean values -->
+<resources>
+ <bool name="large_layout">false</bool>
+</resources>
+</pre>
+
+<p class="code-caption">res/values-large/bools.xml</p>
+<pre>
+<!-- Large screen boolean values -->
+<resources>
+ <bool name="large_layout">true</bool>
+</resources>
+</pre>
+
+<p>Then you can initialize the {@code mIsLargeLayout} value during the activity's
+{@link android.app.Activity#onCreate onCreate()} method:</p>
+
+<pre>
+boolean mIsLargeLayout;
+
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
+}
+</pre>
+
+
+
+<h3 id="ActivityAsDialog">Showing an activity as a dialog on large screens</h3>
+
+<p>Instead of showing a dialog as a fullscreen UI when on small screens, you can accomplish
+the same result by showing an {@link android.app.Activity} as a dialog when on
+large screens. Which approach you choose depends on your app design, but
+showing an activity as a dialog is often useful when your app is already designed for small
+screens and you'd like to improve the experience on tablets by showing a short-lived activity
+as a dialog.</p>
+
+<p>To show an activity as a dialog only when on large screens,
+apply the {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge}
+theme to the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
+<activity>}</a> manifest element:</p>
+
+<pre>
+<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
+</pre>
+
+<p>For more information about styling your activities with themes, see the <a
+href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes</a> guide.</p>
+
+
<h2 id="DismissingADialog">Dismissing a Dialog</h2>
-<p>When you're ready to close your dialog, you can dismiss it by calling
-{@link android.app.Dialog#dismiss()} on the Dialog object.
-If necessary, you can also call {@link android.app.Activity#dismissDialog(int)} from the
-Activity, which effectively calls {@link android.app.Dialog#dismiss()} on the
-Dialog for you.</p>
+<p>When the user touches any of the action buttons created with an
+{@link android.app.AlertDialog.Builder}, the system dismisses the dialog for you.</p>
-<p>If you are using {@link android.app.Activity#onCreateDialog(int)} to manage the state
-of your dialogs (as discussed in the previous section), then every time your dialog is
-dismissed, the state of the Dialog
-object is retained by the Activity. If you decide that you will no longer need this object or
-it's important that the state is cleared, then you should call
-{@link android.app.Activity#removeDialog(int)}. This will remove any internal references
-to the object and if the dialog is showing, it will dismiss it.</p>
+<p>The system also dismisses the dialog when the user touches an item in a dialog list, except
+when the list uses radio buttons or checkboxes. Otherwise, you can manually dismiss your dialog
+by calling {@link android.support.v4.app.DialogFragment#dismiss()} on your {@link
+android.support.v4.app.DialogFragment}.</p>
-<h3>Using dismiss listeners</h3>
+<p>In case you need to perform certain
+actions when the dialog goes away, you can implement the {@link
+android.support.v4.app.DialogFragment#onDismiss onDismiss()} method in your {@link
+android.support.v4.app.DialogFragment}.</p>
-<p>If you'd like your application to perform some procedures the moment that a dialog is dismissed,
-then you should attach an on-dismiss listener to your Dialog.</p>
+<p>You can also <em>cancel</em> a dialog. This is a special event that indicates the user
+explicitly left the dialog without completing the task. This occurs if the user presses the
+<em>Back</em> button, touches the screen outside the dialog area,
+or if you explicitly call {@link android.app.Dialog#cancel()} on the {@link
+android.app.Dialog} (such as in response to a "Cancel" button in the dialog).</p>
-<p>First define the {@link android.content.DialogInterface.OnDismissListener} interface.
-This interface has just one method,
-{@link android.content.DialogInterface.OnDismissListener#onDismiss(DialogInterface)}, which
-will be called when the dialog is dismissed.
-Then simply pass your OnDismissListener implementation to
-{@link android.app.Dialog#setOnDismissListener(DialogInterface.OnDismissListener)
-setOnDismissListener()}.</p>
+<p>As shown in the example above, you can respond to the cancel event by implementing
+{@link android.support.v4.app.DialogFragment#onCancel onCancel()} in your {@link
+android.support.v4.app.DialogFragment} class.</p>
-<p>However, note that dialogs can also be "cancelled." This is a special case that indicates
-the dialog was explicitly cancelled by the user. This will occur if the user presses the
-"back" button to close the dialog, or if the dialog explicitly calls {@link android.app.Dialog#cancel()}
-(perhaps from a "Cancel" button in the dialog). When a dialog is cancelled,
-the OnDismissListener will still be notified, but if you'd like to be informed that the dialog
-was explicitly cancelled (and not dismissed normally), then you should register
-an {@link android.content.DialogInterface.OnCancelListener} with
-{@link android.app.Dialog#setOnCancelListener(DialogInterface.OnCancelListener)
-setOnCancelListener()}.</p>
-
-
-<h2 id="AlertDialog">Creating an AlertDialog</h2>
-
-<p>An {@link android.app.AlertDialog} is an extension of the {@link android.app.Dialog}
-class. It is capable of constructing most dialog user interfaces and is the suggested dialog type.
-You should use it for dialogs that use any of the following features:</p>
-<ul>
- <li>A title</li>
- <li>A text message</li>
- <li>One, two, or three buttons</li>
- <li>A list of selectable items (with optional checkboxes or radio buttons)</li>
-</ul>
-
-<p>To create an AlertDialog, use the {@link android.app.AlertDialog.Builder} subclass.
-Get a Builder with {@link android.app.AlertDialog.Builder#AlertDialog.Builder(Context)} and
-then use the class's public methods to define all of the
-AlertDialog properties. After you're done with the Builder, retrieve the
-AlertDialog object with {@link android.app.AlertDialog.Builder#create()}.</p>
-
-<p>The following topics show how to define various properties of the AlertDialog using the
-AlertDialog.Builder class. If you use any of the following sample code inside your
-{@link android.app.Activity#onCreateDialog(int) onCreateDialog()} callback method,
-you can return the resulting Dialog object to display the dialog.</p>
-
-
-<h3 id="AddingButtons">Adding buttons</h3>
-
-<img src="{@docRoot}images/dialog_buttons.png" alt="" style="float:right" />
-
-<p>To create an AlertDialog with side-by-side buttons like the one shown in the screenshot to the right,
-use the <code>set...Button()</code> methods:</p>
-
-<pre>
-AlertDialog.Builder builder = new AlertDialog.Builder(this);
-builder.setMessage("Are you sure you want to exit?")
- .setCancelable(false)
- .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- MyActivity.this.finish();
- }
- })
- .setNegativeButton("No", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- });
-AlertDialog alert = builder.create();
-</pre>
-
-<p>First, add a message for the dialog with
-{@link android.app.AlertDialog.Builder#setMessage(CharSequence)}. Then, begin
-method-chaining and set the dialog
-to be <em>not cancelable</em> (so the user cannot close the dialog with the back button)
-with {@link android.app.AlertDialog.Builder#setCancelable(boolean)}. For each button,
-use one of the <code>set...Button()</code> methods, such as
-{@link android.app.AlertDialog.Builder#setPositiveButton(CharSequence,DialogInterface.OnClickListener)
-setPositiveButton()}, that accepts the name for the button and a
-{@link android.content.DialogInterface.OnClickListener} that defines the action to take
-when the user selects the button.</p>
-
-<p class="note"><strong>Note:</strong> You can only add one of each button type to the
-AlertDialog. That is, you cannot have more than one "positive" button. This limits the number
-of possible buttons to three: positive, neutral, and negative. These names are technically irrelevant to the
-actual functionality of your buttons, but should help you keep track of which one does what.</p>
-
-
-<h3 id="AddingAList">Adding a list</h3>
-
-<img src="{@docRoot}images/dialog_list.png" alt="" style="float:right" />
-
-<p>To create an AlertDialog with a list of selectable items like the one shown to the right,
-use the <code>setItems()</code> method:</p>
-
-<pre>
-final CharSequence[] items = {"Red", "Green", "Blue"};
-
-AlertDialog.Builder builder = new AlertDialog.Builder(this);
-builder.setTitle("Pick a color");
-builder.setItems(items, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
- }
-});
-AlertDialog alert = builder.create();
-</pre>
-
-<p>First, add a title to the dialog with
-{@link android.app.AlertDialog.Builder#setTitle(CharSequence)}.
-Then, add a list of selectable items with
-{@link android.app.AlertDialog.Builder#setItems(CharSequence[],DialogInterface.OnClickListener)
-setItems()}, which accepts the array of items to display and a
-{@link android.content.DialogInterface.OnClickListener} that defines the action to take
-when the user selects an item.</p>
-
-
-<h4>Adding checkboxes and radio buttons</h4>
-
-<img src="{@docRoot}images/dialog_singlechoicelist.png" alt="" style="float:right" />
-
-<p>To create a list of multiple-choice items (checkboxes) or
-single-choice items (radio buttons) inside the dialog, use the
-{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String,
-DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} and
-{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener)
-setSingleChoiceItems()} methods, respectively.
-If you create one of these selectable lists in the
-{@link android.app.Activity#onCreateDialog(int) onCreateDialog()} callback method,
-Android manages the state of the list for you. As long as the Activity is active,
-the dialog remembers the items that were previously selected, but when the user exits the
-Activity, the selection is lost.
-
-<p class="note"><strong>Note:</strong> To save the selection when the user leaves or
-pauses the Activity, you must properly save and restore the setting throughout
-the <a href="{@docRoot}guide/components/activities.html#Lifecycle">activity lifecycle</a>.
-To permanently save the selections, even when the Activity process is completely shutdown,
-you need to save the settings
-with one of the <a href="{@docRoot}guide/topics/data/data-storage.html">Data
-Storage</a> techniques.</p>
-
-<p>To create an AlertDialog with a list of single-choice items like the one shown to the right,
-use the same code from the previous example, but replace the <code>setItems()</code> method with
-{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener)
-setSingleChoiceItems()}:</p>
-
-<pre>
-final CharSequence[] items = {"Red", "Green", "Blue"};
-
-AlertDialog.Builder builder = new AlertDialog.Builder(this);
-builder.setTitle("Pick a color");
-builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
- }
-});
-AlertDialog alert = builder.create();
-</pre>
-
-<p>The second parameter in the
-{@link android.app.AlertDialog.Builder#setSingleChoiceItems(CharSequence[],int,DialogInterface.OnClickListener)
-setSingleChoiceItems()} method is an integer value for the <var>checkedItem</var>, which indicates the
-zero-based list position of the default selected item. Use "-1" to indicate that no item should be
-selected by default.</p>
-
-
-<h2 id="ProgressDialog">Creating a ProgressDialog</h2>
-
-<img src="{@docRoot}images/dialog_progress_spinning.png" alt="" style="float:right" />
-
-<p>A {@link android.app.ProgressDialog} is an extension of the {@link android.app.AlertDialog}
-class that can display a progress animation in the form of a spinning wheel, for a task with
-progress that's undefined, or a progress bar, for a task that has a defined progression.
-The dialog can also provide buttons, such as one to cancel a download.</p>
-
-<p>Opening a progress dialog can be as simple as calling
-{@link android.app.ProgressDialog#show(Context,CharSequence,CharSequence)
-ProgressDialog.show()}. For example, the progress dialog shown to the right can be
-easily achieved without managing the dialog through the
-{@link android.app.Activity#onCreateDialog(int)} callback,
-as shown here:</p>
-
-<pre>
-ProgressDialog dialog = ProgressDialog.show(MyActivity.this, "",
- "Loading. Please wait...", true);
-</pre>
-
-<p>The first parameter is the application {@link android.content.Context},
-the second is a title for the dialog (left empty), the third is the message,
-and the last parameter is whether the progress
-is indeterminate (this is only relevant when creating a progress bar, which is
-discussed in the next section).
-</p>
-
-<p>The default style of a progress dialog is the spinning wheel.
-If you want to create a progress bar that shows the loading progress with granularity,
-some more code is required, as discussed in the next section.</p>
-
-
-<h3 id="ShowingAProgressBar">Showing a progress bar</h3>
-
-<img src="/images/dialog_progress_bar.png" alt="" style="float:right" />
-
-<p>To show the progression with an animated progress bar:</p>
-
-<ol>
- <li>Initialize the
- ProgressDialog with the class constructor,
- {@link android.app.ProgressDialog#ProgressDialog(Context)}.</li>
- <li>Set the progress style to "STYLE_HORIZONTAL" with
- {@link android.app.ProgressDialog#setProgressStyle(int)} and
- set any other properties, such as the message.</li>
- <li>When you're ready to show the dialog, call
- {@link android.app.Dialog#show()} or return the ProgressDialog from the
- {@link android.app.Activity#onCreateDialog(int)} callback.</li>
- <li>You can increment the amount of progress displayed
- in the bar by calling either {@link android.app.ProgressDialog#setProgress(int)} with a value for
- the total percentage completed so far or {@link android.app.ProgressDialog#incrementProgressBy(int)}
- with an incremental value to add to the total percentage completed so far.</li>
-</ol>
-
-<p>For example, your setup might look like this:</p>
-<pre>
-ProgressDialog progressDialog;
-progressDialog = new ProgressDialog(mContext);
-progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
-progressDialog.setMessage("Loading...");
-progressDialog.setCancelable(false);
-</pre>
-
-<p>The setup is simple. Most of the code needed to create a progress dialog is actually
-involved in the process that updates it. You might find that it's
-necessary to create a second thread in your application for this work and then report the progress
-back to the Activity's UI thread with a {@link android.os.Handler} object.
-If you're not familiar with using additional
-threads with a Handler, see the example Activity below that uses a second thread to
-increment a progress dialog managed by the Activity.</p>
-
-<script type="text/javascript">
-function toggleDiv(link) {
- var toggleable = $(link).parent();
- if (toggleable.hasClass("closed")) {
- $(".toggleme", toggleable).slideDown("fast");
- toggleable.removeClass("closed");
- toggleable.addClass("open");
- $(".toggle-img", toggleable).attr("title", "hide").attr("src", "/assets/images/triangle-opened.png");
- } else {
- $(".toggleme", toggleable).slideUp("fast");
- toggleable.removeClass("open");
- toggleable.addClass("closed");
- $(".toggle-img", toggleable).attr("title", "show").attr("src", "/assets/images/triangle-closed.png");
- }
- return false;
-}
-</script>
-<style>
-.toggleme {
- padding:0 0 1px 0;
-}
-.toggleable a {
- text-decoration:none;
-}
-.toggleable.closed .toggleme {
- display:none;
-}
-#jd-content .toggle-img {
- margin:0;
-}
-</style>
-
-<div class="toggleable closed">
- <a href="#" onclick="return toggleDiv(this)">
- <img src="/assets/images/triangle-closed.png" class="toggle-img" />
- <strong>Example ProgressDialog with a second thread</strong></a>
- <div class="toggleme">
- <p>This example uses a second thread to track the progress of a process (which actually just
-counts up to 100). The thread sends a {@link android.os.Message} back to the main
-Activity through a {@link android.os.Handler} each time progress is made. The main Activity then updates the
-ProgressDialog.</p>
-
-<pre>
-package com.example.progressdialog;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-
-public class NotificationTest extends Activity {
- static final int PROGRESS_DIALOG = 0;
- Button button;
- ProgressThread progressThread;
- ProgressDialog progressDialog;
-
- /** Called when the activity is first created. */
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- // Setup the button that starts the progress dialog
- button = (Button) findViewById(R.id.progressDialog);
- button.setOnClickListener(new OnClickListener(){
- public void onClick(View v) {
- showDialog(PROGRESS_DIALOG);
- }
- });
- }
-
- protected Dialog onCreateDialog(int id) {
- switch(id) {
- case PROGRESS_DIALOG:
- progressDialog = new ProgressDialog(NotificationTest.this);
- progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- progressDialog.setMessage("Loading...");
- return progressDialog;
- default:
- return null;
- }
- }
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog) {
- switch(id) {
- case PROGRESS_DIALOG:
- progressDialog.setProgress(0);
- progressThread = new ProgressThread(handler);
- progressThread.start();
- }
-
- // Define the Handler that receives messages from the thread and update the progress
- final Handler handler = new Handler() {
- public void handleMessage(Message msg) {
- int total = msg.arg1;
- progressDialog.setProgress(total);
- if (total >= 100){
- dismissDialog(PROGRESS_DIALOG);
- progressThread.setState(ProgressThread.STATE_DONE);
- }
- }
- };
-
- /** Nested class that performs progress calculations (counting) */
- private class ProgressThread extends Thread {
- Handler mHandler;
- final static int STATE_DONE = 0;
- final static int STATE_RUNNING = 1;
- int mState;
- int total;
-
- ProgressThread(Handler h) {
- mHandler = h;
- }
-
- public void run() {
- mState = STATE_RUNNING;
- total = 0;
- while (mState == STATE_RUNNING) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- Log.e("ERROR", "Thread Interrupted");
- }
- Message msg = mHandler.obtainMessage();
- msg.arg1 = total;
- mHandler.sendMessage(msg);
- total++;
- }
- }
-
- /* sets the current state for the thread,
- * used to stop the thread */
- public void setState(int state) {
- mState = state;
- }
- }
-}
-</pre>
- </div> <!-- end toggleme -->
-</div> <!-- end toggleable -->
-
-
-
-<h2 id="CustomDialog">Creating a Custom Dialog</h2>
-
-<img src="{@docRoot}images/dialog_custom.png" alt="" style="float:right" />
-
-<p>If you want a customized design for a dialog, you can create your own layout
-for the dialog window with layout and widget elements.
-After you've defined your layout, pass the root View object or
-layout resource ID to {@link android.app.Dialog#setContentView(View)}.</p>
-
-<p>For example, to create the dialog shown to the right:</p>
-
-<ol>
- <li>Create an XML layout saved as <code>custom_dialog.xml</code>:
-<pre>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout_root"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:padding="10dp"
- >
- <ImageView android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_marginRight="10dp"
- />
- <TextView android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:textColor="#FFF"
- />
-</LinearLayout>
-</pre>
-
- <p>This XML defines an {@link android.widget.ImageView} and a {@link android.widget.TextView}
- inside a {@link android.widget.LinearLayout}.</p>
- <li>Set the above layout as the dialog's content view and define the content
- for the ImageView and TextView elements:</p>
-<pre>
-Context mContext = getApplicationContext();
-Dialog dialog = new Dialog(mContext);
-
-dialog.setContentView(R.layout.custom_dialog);
-dialog.setTitle("Custom Dialog");
-
-TextView text = (TextView) dialog.findViewById(R.id.text);
-text.setText("Hello, this is a custom dialog!");
-ImageView image = (ImageView) dialog.findViewById(R.id.image);
-image.setImageResource(R.drawable.android);
-</pre>
-
- <p>After you instantiate the Dialog, set your custom layout as the dialog's content view with
- {@link android.app.Dialog#setContentView(int)}, passing it the layout resource ID.
- Now that the Dialog has a defined layout, you can capture View objects from the layout with
- {@link android.app.Dialog#findViewById(int)} and modify their content.</p>
- </li>
-
- <li>That's it. You can now show the dialog as described in
- <a href="#ShowingADialog">Showing A Dialog</a>.</li>
-</ol>
-
-<p>A dialog made with the base Dialog class must have a title. If you don't call
-{@link android.app.Dialog#setTitle(CharSequence) setTitle()}, then the space used for the title
-remains empty, but still visible. If you don't want
-a title at all, then you should create your custom dialog using the
-{@link android.app.AlertDialog} class. However, because an AlertDialog is created easiest with
-the {@link android.app.AlertDialog.Builder} class, you do not have access to the
-{@link android.app.Dialog#setContentView(int)} method used above. Instead, you must use
-{@link android.app.AlertDialog.Builder#setView(View)}. This method accepts a {@link android.view.View} object,
-so you need to inflate the layout's root View object from
-XML.</p>
-
-<p>To inflate the XML layout, retrieve the {@link android.view.LayoutInflater} with
-{@link android.app.Activity#getLayoutInflater()}
-(or {@link android.content.Context#getSystemService(String) getSystemService()}),
-and then call
-{@link android.view.LayoutInflater#inflate(int, ViewGroup)}, where the first parameter
-is the layout resource ID and the second is the ID of the root View. At this point, you can use
-the inflated layout to find View objects in the layout and define the content for the
-ImageView and TextView elements. Then instantiate the AlertDialog.Builder and set the
-inflated layout for the dialog with {@link android.app.AlertDialog.Builder#setView(View)}.</p>
-
-<p>Here's an example, creating a custom layout in an AlertDialog:</p>
-
-<pre>
-AlertDialog.Builder builder;
-AlertDialog alertDialog;
-
-Context mContext = getApplicationContext();
-LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
-View layout = inflater.inflate(R.layout.custom_dialog,
- (ViewGroup) findViewById(R.id.layout_root));
-
-TextView text = (TextView) layout.findViewById(R.id.text);
-text.setText("Hello, this is a custom dialog!");
-ImageView image = (ImageView) layout.findViewById(R.id.image);
-image.setImageResource(R.drawable.android);
-
-builder = new AlertDialog.Builder(mContext);
-builder.setView(layout);
-alertDialog = builder.create();
-</pre>
-
-<p>Using an AlertDialog for your custom layout lets you
-take advantage of built-in AlertDialog features like managed buttons,
-selectable lists, a title, an icon and so on.</p>
-
-<p>For more information, refer to the reference documentation for the
-{@link android.app.Dialog} and {@link android.app.AlertDialog.Builder}
-classes.</p>
-
+<p class="note"><strong>Note:</strong> The system calls
+{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} upon each event that
+invokes the {@link android.support.v4.app.DialogFragment#onCancel onCancel()} callback. However,
+if you call {@link android.app.Dialog#dismiss Dialog.dismiss()} or {@link
+android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()},
+the system calls {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} <em>but
+not</em> {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. So you should generally
+call {@link android.support.v4.app.DialogFragment#dismiss dismiss()} when the user presses the
+<em>positive</em> button in your dialog in order to remove the dialog from view.</p>
diff --git a/docs/html/images/dialog_buttons.png b/docs/html/images/dialog_buttons.png
deleted file mode 100755
index 81aaec4..0000000
--- a/docs/html/images/dialog_buttons.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/dialog_custom.png b/docs/html/images/dialog_custom.png
deleted file mode 100755
index b2523fd..0000000
--- a/docs/html/images/dialog_custom.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/dialog_list.png b/docs/html/images/dialog_list.png
deleted file mode 100755
index f2736bf..0000000
--- a/docs/html/images/dialog_list.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/dialog_progress_bar.png b/docs/html/images/dialog_progress_bar.png
deleted file mode 100755
index 3e74419..0000000
--- a/docs/html/images/dialog_progress_bar.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/dialog_progress_spinning.png b/docs/html/images/dialog_progress_spinning.png
deleted file mode 100755
index 501f4802..0000000
--- a/docs/html/images/dialog_progress_spinning.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/dialog_singlechoicelist.png b/docs/html/images/dialog_singlechoicelist.png
deleted file mode 100755
index 90629f0..0000000
--- a/docs/html/images/dialog_singlechoicelist.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/distribute/instapaper.png b/docs/html/images/distribute/instapaper.png
new file mode 100644
index 0000000..ffbf052
--- /dev/null
+++ b/docs/html/images/distribute/instapaper.png
Binary files differ
diff --git a/docs/html/images/distribute/mint.png b/docs/html/images/distribute/mint.png
new file mode 100644
index 0000000..50d90d5
--- /dev/null
+++ b/docs/html/images/distribute/mint.png
Binary files differ
diff --git a/docs/html/images/distribute/tinyvillage.png b/docs/html/images/distribute/tinyvillage.png
new file mode 100644
index 0000000..cde77f0
--- /dev/null
+++ b/docs/html/images/distribute/tinyvillage.png
Binary files differ
diff --git a/docs/html/images/ui-ex-multi-pane.png b/docs/html/images/ui-ex-multi-pane.png
new file mode 100644
index 0000000..188bb8b
--- /dev/null
+++ b/docs/html/images/ui-ex-multi-pane.png
Binary files differ
diff --git a/docs/html/images/ui-ex-single-panes.png b/docs/html/images/ui-ex-single-panes.png
new file mode 100644
index 0000000..dff24a7
--- /dev/null
+++ b/docs/html/images/ui-ex-single-panes.png
Binary files differ
diff --git a/docs/html/images/ui/dialog_buttons.png b/docs/html/images/ui/dialog_buttons.png
new file mode 100644
index 0000000..ed952a1
--- /dev/null
+++ b/docs/html/images/ui/dialog_buttons.png
Binary files differ
diff --git a/docs/html/images/ui/dialog_checkboxes.png b/docs/html/images/ui/dialog_checkboxes.png
new file mode 100644
index 0000000..8f272e5
--- /dev/null
+++ b/docs/html/images/ui/dialog_checkboxes.png
Binary files differ
diff --git a/docs/html/images/ui/dialog_custom.png b/docs/html/images/ui/dialog_custom.png
new file mode 100644
index 0000000..244473b
--- /dev/null
+++ b/docs/html/images/ui/dialog_custom.png
Binary files differ
diff --git a/docs/html/images/ui/dialog_list.png b/docs/html/images/ui/dialog_list.png
new file mode 100644
index 0000000..437fc74
--- /dev/null
+++ b/docs/html/images/ui/dialog_list.png
Binary files differ
diff --git a/docs/html/images/ui/dialogs.png b/docs/html/images/ui/dialogs.png
new file mode 100644
index 0000000..d45b0b5
--- /dev/null
+++ b/docs/html/images/ui/dialogs.png
Binary files differ
diff --git a/docs/html/images/ui/dialogs_regions.png b/docs/html/images/ui/dialogs_regions.png
new file mode 100644
index 0000000..2bfc1a4
--- /dev/null
+++ b/docs/html/images/ui/dialogs_regions.png
Binary files differ
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 0952885..4ad1353 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -235,41 +235,31 @@
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>training/multiscreen/index.html">
- <span class="en">Designing for Multiple Screens</span>
- <span class="es">Cómo diseñar aplicaciones para varias pantallas</span>
- <span class="ja">複数画面のデザイン</span>
- <span class="ko">Designing for Multiple Screens</span>
- <span class="ru">Designing for Multiple Screens</span>
- <span class="zh-CN">针对多种屏幕进行设计</span>
- </a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>training/multiscreen/index.html"
+ zh-CN-lang="针对多种屏幕进行设计"
+ ja-lang="複数画面のデザイン"
+ es-lang="Cómo diseñar aplicaciones para varias pantallas"
+ >Designing for Multiple Screens</a>
+ </div>
<ul>
- <li><a href="<?cs var:toroot ?>training/multiscreen/screensizes.html">
- <span class="en">Supporting Different Screen Sizes</span>
- <span class="es">Cómo admitir varios tamaños de pantalla</span>
- <span class="ja">さまざまな画面サイズのサポート</span>
- <span class="ko">다양한 화면 크기 지원</span>
- <span class="ru">Supporting Different Screen Sizes</span>
- <span class="zh-CN">支持各种屏幕尺寸</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/multiscreen/screensizes.html"
+ zh-CN-lang="支持各种屏幕尺寸"
+ ko-lang="다양한 화면 크기 지원"
+ ja-lang="さまざまな画面サイズのサポート"
+ es-lang="Cómo admitir varios tamaños de pantalla"
+ >Designing for Multiple Screens</a>
</li>
- <li><a href="<?cs var:toroot ?>training/multiscreen/screendensities.html">
- <span class="en">Supporting Different Screen Densities</span>
- <span class="es">Cómo admitir varias densidades de pantalla</span>
- <span class="ja">さまざまな画面密度のサポート</span>
- <span class="ko">Supporting Different Screen Densities</span>
- <span class="ru">Supporting Different Screen Densities</span>
- <span class="zh-CN">支持各种屏幕密度</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/multiscreen/screendensities.html"
+ zh-CN-lang="支持各种屏幕密度"
+ ja-lang="さまざまな画面密度のサポート"
+ es-lang="Cómo admitir varias densidades de pantalla"
+ >Supporting Different Screen Densities</a>
</li>
- <li><a href="<?cs var:toroot ?>training/multiscreen/adaptui.html">
- <span class="en">Implementing Adaptive UI Flows</span>
- <span class="es">Cómo implementar interfaces de usuario adaptables</span>
- <span class="ja">順応性のある UI フローの実装</span>
- <span class="ko">Implementing Adaptive UI Flows</span>
- <span class="ru">Implementing Adaptive UI Flows</span>
- <span class="zh-CN">实施自适应用户界面流程</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/multiscreen/adaptui.html"
+ zh-CN-lang="实施自适应用户界面流程"
+ ja-lang="順応性のある UI フローの実装"
+ es-lang="Cómo implementar interfaces de usuario adaptables"
+ >Implementing Adaptive UI Flows</a>
</li>
</ul>
</li>
@@ -319,50 +309,36 @@
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>training/monitoring-device-state/index.html">
- <span class="en">Optimizing Battery Life</span>
- <span class="es">Cómo optimizar la duración de la batería</span>
- <span class="ja">電池消費量の最適化</span>
- <span class="ko">Optimizing Battery Life</span>
- <span class="ru">Optimizing Battery Life</span>
- <span class="zh-CN">优化电池使用时间</span>
- </a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>training/monitoring-device-state/index.html"
+ zh-CN-lang="优化电池使用时间"
+ ja-lang="電池消費量の最適化"
+ es-lang="Cómo optimizar la duración de la batería"
+ >Optimizing Battery Life</a>
+ </div>
<ul>
- <li><a href="<?cs var:toroot ?>training/monitoring-device-state/battery-monitoring.html">
- <span class="en">Monitoring the Battery Level and Charging State</span>
- <span class="es">Cómo controlar el nivel de batería y el estado de carga</span>
- <span class="ja">電池残量と充電状態の監視</span>
- <span class="ko">Monitoring the Battery Level and Charging State</span>
- <span class="ru">Monitoring the Battery Level and Charging State</span>
- <span class="zh-CN">监控电池电量和充电状态</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/monitoring-device-state/battery-monitoring.html"
+ zh-CN-lang="监控电池电量和充电状态"
+ ja-lang="電池残量と充電状態の監視"
+ es-lang="Cómo controlar el nivel de batería y el estado de carga"
+ >Monitoring the Battery Level and Charging State</a>
</li>
- <li><a href="<?cs var:toroot ?>training/monitoring-device-state/docking-monitoring.html">
- <span class="en">Determining and Monitoring the Docking State and Type</span>
- <span class="es">Cómo determinar y controlar el tipo de conector y el estado de la conexión</span>
- <span class="ja">ホルダーの装着状態とタイプの特定と監視</span>
- <span class="ko">Determining and Monitoring the Docking State and Type</span>
- <span class="ru">Determining and Monitoring the Docking State and Type</span>
- <span class="zh-CN">确定和监控基座对接状态和类型</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/monitoring-device-state/docking-monitoring.html"
+ zh-CN-lang="确定和监控基座对接状态和类型"
+ ja-lang="ホルダーの装着状態とタイプの特定と監視"
+ es-lang="Cómo determinar y controlar el tipo de conector y el estado de la conexión"
+ >Determining and Monitoring the Docking State and Type</a>
</li>
- <li><a href="<?cs var:toroot ?>training/monitoring-device-state/connectivity-monitoring.html">
- <span class="en">Determining and Monitoring the Connectivity Status</span>
- <span class="es">Cómo determinar y controlar el estado de la conectividad</span>
- <span class="ja">接続状態の特定と監視</span>
- <span class="ko">Determining and Monitoring the Connectivity Status</span>
- <span class="ru">Determining and Monitoring the Connectivity Status</span>
- <span class="zh-CN">确定和监控网络连接状态</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/monitoring-device-state/connectivity-monitoring.html"
+ zh-CN-lang="确定和监控网络连接状态"
+ ja-lang="接続状態の特定と監視"
+ es-lang="Cómo determinar y controlar el estado de la conectividad"
+ >Determining and Monitoring the Connectivity Status</a>
</li>
- <li><a href="<?cs var:toroot ?>training/monitoring-device-state/manifest-receivers.html">
- <span class="en">Manipulating Broadcast Receivers On Demand</span>
- <span class="es">Cómo manipular los receptores de emisión bajo demanda</span>
- <span class="ja">オンデマンドでのブロードキャスト レシーバ操作</span>
- <span class="ko">Manipulating Broadcast Receivers On Demand</span>
- <span class="ru">Manipulating Broadcast Receivers On Demand</span>
- <span class="zh-CN">根据需要操作广播接收器</span>
- </a>
+ <li><a href="<?cs var:toroot ?>training/monitoring-device-state/manifest-receivers.html"
+ zh-CN-lang="根据需要操作广播接收器"
+ ja-lang="オンデマンドでのブロードキャスト レシーバ操作"
+ es-lang="Cómo manipular los receptores de emisión bajo demanda"
+ >Manipulating Broadcast Receivers On Demand</a>
</li>
</ul>
</li>
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 22aa29c..8374b10 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -478,6 +478,9 @@
mFillPaint.setAlpha(currFillAlpha);
mFillPaint.setDither(mDither);
mFillPaint.setColorFilter(mColorFilter);
+ if (mColorFilter != null && !mGradientState.mHasSolidColor) {
+ mFillPaint.setColor(0xff000000);
+ }
if (haveStroke) {
mStrokePaint.setAlpha(currStrokeAlpha);
mStrokePaint.setDither(mDither);
@@ -739,6 +742,9 @@
mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
colors, st.mPositions, Shader.TileMode.CLAMP));
+ if (!mGradientState.mHasSolidColor) {
+ mFillPaint.setColor(0xff000000);
+ }
} else if (st.mGradient == RADIAL_GRADIENT) {
x0 = r.left + (r.right - r.left) * st.mCenterX;
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -748,6 +754,9 @@
mFillPaint.setShader(new RadialGradient(x0, y0,
level * st.mGradientRadius, colors, null,
Shader.TileMode.CLAMP));
+ if (!mGradientState.mHasSolidColor) {
+ mFillPaint.setColor(0xff000000);
+ }
} else if (st.mGradient == SWEEP_GRADIENT) {
x0 = r.left + (r.right - r.left) * st.mCenterX;
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -778,6 +787,9 @@
}
mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));
+ if (!mGradientState.mHasSolidColor) {
+ mFillPaint.setColor(0xff000000);
+ }
}
}
}
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
index 61e5d4f..2a04b42 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
@@ -69,13 +69,13 @@
/**
* Set the radius of the Blur.
*
- * Supported range 0-25
+ * Supported range 0 < radius <= 25
*
* @param radius The radius of the blur
*/
public void setRadius(float radius) {
- if (radius < 0 || radius > 25) {
- throw new RSIllegalArgumentException("Radius out of range (0-25).");
+ if (radius <= 0 || radius > 25) {
+ throw new RSIllegalArgumentException("Radius out of range (0 < r <= 25).");
}
setVar(0, radius);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index fdb6818..61418fb 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1737,7 +1737,8 @@
streamState.readSettings();
// unmute stream that was muted but is not affect by mute anymore
- if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
+ if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType) &&
+ !isStreamMutedByRingerMode(streamType)) {
int size = streamState.mDeathHandlers.size();
for (int i = 0; i < size; i++) {
streamState.mDeathHandlers.get(i).mMuteCount = 1;
@@ -2591,6 +2592,18 @@
public synchronized void readSettings() {
int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+ // do not read system stream volume from settings: this stream is always aliased
+ // to another stream type and its volume is never persisted. Values in settings can
+ // only be stale values
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
+ (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
+ mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
+ 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
+ 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+ return;
+ }
+
for (int i = 0; remainingDevices != 0; i++) {
int device = (1 << i);
if ((device & remainingDevices) == 0) {
@@ -2621,11 +2634,8 @@
// a last audible index of 0 should never be stored for ring and notification
// streams on phones (voice capable devices).
- // same for system stream on phones and tablets
- if ((lastAudibleIndex == 0) &&
- ((mVoiceCapable &&
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
+ if ((lastAudibleIndex == 0) && mVoiceCapable &&
+ (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
// Correct the data base
sendMsg(mAudioHandler,
@@ -2639,11 +2649,9 @@
mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
// the initial index should never be 0 for ring and notification streams on phones
// (voice capable devices) if not in silent or vibrate mode.
- // same for system stream on phones and tablets
if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
- ((mVoiceCapable &&
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
+ mVoiceCapable &&
+ (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
index = lastAudibleIndex;
// Correct the data base
sendMsg(mAudioHandler,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 1096540..ba7501b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -46,7 +46,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -64,25 +63,35 @@
private static final String KEY_SYSTEM = "system";
private static final String KEY_SECURE = "secure";
+ private static final String KEY_GLOBAL = "global";
private static final String KEY_LOCALE = "locale";
- //Version 2 adds STATE_WIFI_CONFIG
- private static final int STATE_VERSION_1 = 1;
- private static final int STATE_VERSION_1_SIZE = 4;
-
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 2;
+ private static final int STATE_VERSION = 3;
+ // Slots in the checksum array. Never insert new items in the middle
+ // of this array; new slots must be appended.
private static final int STATE_SYSTEM = 0;
private static final int STATE_SECURE = 1;
private static final int STATE_LOCALE = 2;
private static final int STATE_WIFI_SUPPLICANT = 3;
private static final int STATE_WIFI_CONFIG = 4;
- private static final int STATE_SIZE = 5; // The number of state items
+ private static final int STATE_GLOBAL = 5;
+
+ private static final int STATE_SIZE = 6; // The current number of state items
+
+ // Number of entries in the checksum array at various version numbers
+ private static final int STATE_SIZES[] = {
+ 0,
+ 4, // version 1
+ 5, // version 2 added STATE_WIFI_CONFIG
+ STATE_SIZE // version 3 added STATE_GLOBAL
+ };
// Versioning of the 'full backup' format
- private static final int FULL_BACKUP_VERSION = 1;
+ private static final int FULL_BACKUP_VERSION = 2;
+ private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
@@ -257,6 +266,7 @@
byte[] systemSettingsData = getSystemSettings();
byte[] secureSettingsData = getSecureSettings();
+ byte[] globalSettingsData = getGlobalSettings();
byte[] locale = mSettingsHelper.getLocaleData();
byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
byte[] wifiConfigData = getFileData(mWifiConfigFile);
@@ -267,6 +277,8 @@
writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
stateChecksums[STATE_SECURE] =
writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+ stateChecksums[STATE_GLOBAL] =
+ writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, secureSettingsData, data);
stateChecksums[STATE_LOCALE] =
writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
stateChecksums[STATE_WIFI_SUPPLICANT] =
@@ -283,14 +295,18 @@
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
+ HashSet<String> movedToGlobal = new HashSet<String>();
+ Settings.System.getMovedKeys(movedToGlobal);
+ Settings.Secure.getMovedKeys(movedToGlobal);
+
while (data.readNextHeader()) {
final String key = data.getKey();
final int size = data.getDataSize();
if (KEY_SYSTEM.equals(key)) {
- restoreSettings(data, Settings.System.CONTENT_URI);
+ restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
mSettingsHelper.applyAudioSettings();
} else if (KEY_SECURE.equals(key)) {
- restoreSettings(data, Settings.Secure.CONTENT_URI);
+ restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
} else if (KEY_WIFI_SUPPLICANT.equals(key)) {
int retainedWifiState = enableWifi(false);
restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, data);
@@ -317,6 +333,7 @@
public void onFullBackup(FullBackupDataOutput data) throws IOException {
byte[] systemSettingsData = getSystemSettings();
byte[] secureSettingsData = getSecureSettings();
+ byte[] globalSettingsData = getGlobalSettings();
byte[] locale = mSettingsHelper.getLocaleData();
byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
byte[] wifiConfigData = getFileData(mWifiConfigFile);
@@ -339,6 +356,9 @@
if (DEBUG_BACKUP) Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
out.writeInt(secureSettingsData.length);
out.write(secureSettingsData);
+ if (DEBUG_BACKUP) Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
+ out.writeInt(globalSettingsData.length);
+ out.write(globalSettingsData);
if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
out.writeInt(locale.length);
out.write(locale);
@@ -371,20 +391,35 @@
int version = in.readInt();
if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
- if (version == FULL_BACKUP_VERSION) {
+ if (version <= FULL_BACKUP_VERSION) {
+ // Generate the moved-to-global lookup table
+ HashSet<String> movedToGlobal = new HashSet<String>();
+ Settings.System.getMovedKeys(movedToGlobal);
+ Settings.Secure.getMovedKeys(movedToGlobal);
+
// system settings data first
int nBytes = in.readInt();
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
byte[] buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI);
+ restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
// secure settings
nBytes = in.readInt();
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI);
+ restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
+
+ // Global only if sufficiently new
+ if (version >= FULL_BACKUP_ADDED_GLOBAL) {
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ in.readFully(buffer, 0, nBytes);
+ movedToGlobal.clear(); // no redirection; this *is* the global namespace
+ restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
+ }
// locale
nBytes = in.readInt();
@@ -430,14 +465,8 @@
try {
int stateVersion = dataInput.readInt();
- if (stateVersion == STATE_VERSION_1) {
- for (int i = 0; i < STATE_VERSION_1_SIZE; i++) {
- stateChecksums[i] = dataInput.readLong();
- }
- } else if (stateVersion == STATE_VERSION) {
- for (int i = 0; i < STATE_SIZE; i++) {
- stateChecksums[i] = dataInput.readLong();
- }
+ for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
+ stateChecksums[i] = dataInput.readLong();
}
} catch (EOFException eof) {
// With the default 0 checksum we'll wind up forcing a backup of
@@ -496,7 +525,18 @@
}
}
- private void restoreSettings(BackupDataInput data, Uri contentUri) {
+ private byte[] getGlobalSettings() {
+ Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
+ null, null);
+ try {
+ return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void restoreSettings(BackupDataInput data, Uri contentUri,
+ HashSet<String> movedToGlobal) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
@@ -504,20 +544,23 @@
Log.e(TAG, "Couldn't read entity data");
return;
}
- restoreSettings(settings, settings.length, contentUri);
+ restoreSettings(settings, settings.length, contentUri, movedToGlobal);
}
- private void restoreSettings(byte[] settings, int bytes, Uri contentUri) {
+ private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
+ HashSet<String> movedToGlobal) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
- // Figure out the white list.
+ // Figure out the white list and redirects to the global table.
String[] whitelist = null;
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
whitelist = Settings.Secure.SETTINGS_TO_BACKUP;
} else if (contentUri.equals(Settings.System.CONTENT_URI)) {
whitelist = Settings.System.SETTINGS_TO_BACKUP;
+ } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
+ whitelist = Settings.Global.SETTINGS_TO_BACKUP;
} else {
throw new IllegalArgumentException("Unknown URI: " + contentUri);
}
@@ -556,15 +599,20 @@
continue;
}
+ final Uri destination = (movedToGlobal.contains(key))
+ ? Settings.Global.CONTENT_URI
+ : contentUri;
+
+ // The helper doesn't care what namespace the keys are in
if (settingsHelper.restoreValue(key, value)) {
contentValues.clear();
contentValues.put(Settings.NameValueTable.NAME, key);
contentValues.put(Settings.NameValueTable.VALUE, value);
- getContentResolver().insert(contentUri, contentValues);
+ getContentResolver().insert(destination, contentValues);
}
if (DEBUG || true) {
- Log.d(TAG, "Restored setting: " + key + "=" + value);
+ Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
}
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index cc6656d..ad35f7f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -110,158 +110,12 @@
// table, shared across all users
// These must match Settings.Secure.MOVED_TO_GLOBAL
sSecureGlobalKeys = new HashSet<String>();
- sSecureGlobalKeys.add(Settings.Global.ADB_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.ASSISTED_GPS_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.BLUETOOTH_ON);
- sSecureGlobalKeys.add(Settings.Global.CDMA_CELL_BROADCAST_SMS);
- sSecureGlobalKeys.add(Settings.Global.CDMA_ROAMING_MODE);
- sSecureGlobalKeys.add(Settings.Global.CDMA_SUBSCRIPTION_MODE);
- sSecureGlobalKeys.add(Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE);
- sSecureGlobalKeys.add(Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI);
- sSecureGlobalKeys.add(Settings.Global.DATA_ROAMING);
- sSecureGlobalKeys.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.DEVICE_PROVISIONED);
- sSecureGlobalKeys.add(Settings.Global.DISPLAY_DENSITY_FORCED);
- sSecureGlobalKeys.add(Settings.Global.DISPLAY_SIZE_FORCED);
- sSecureGlobalKeys.add(Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
- sSecureGlobalKeys.add(Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
- sSecureGlobalKeys.add(Settings.Global.INSTALL_NON_MARKET_APPS);
- sSecureGlobalKeys.add(Settings.Global.MOBILE_DATA);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_DEV_BUCKET_DURATION);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_DEV_DELETE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_DEV_PERSIST_BYTES);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_DEV_ROTATE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_POLL_INTERVAL);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_REPORT_XT_OVER_DEV);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_SAMPLE_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_BUCKET_DURATION);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_DELETE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_PERSIST_BYTES);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_ROTATE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_TAG_DELETE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES);
- sSecureGlobalKeys.add(Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE);
- sSecureGlobalKeys.add(Settings.Global.NETWORK_PREFERENCE);
- sSecureGlobalKeys.add(Settings.Global.NITZ_UPDATE_DIFF);
- sSecureGlobalKeys.add(Settings.Global.NITZ_UPDATE_SPACING);
- sSecureGlobalKeys.add(Settings.Global.NTP_SERVER);
- sSecureGlobalKeys.add(Settings.Global.NTP_TIMEOUT);
- sSecureGlobalKeys.add(Settings.Global.PDP_WATCHDOG_ERROR_POLL_COUNT);
- sSecureGlobalKeys.add(Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS);
- sSecureGlobalKeys.add(Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT);
- sSecureGlobalKeys.add(Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS);
- sSecureGlobalKeys.add(Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT);
- sSecureGlobalKeys.add(Settings.Global.SAMPLING_PROFILER_MS);
- sSecureGlobalKeys.add(Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL);
- sSecureGlobalKeys.add(Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST);
- sSecureGlobalKeys.add(Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL);
- sSecureGlobalKeys.add(Settings.Global.TETHER_DUN_APN);
- sSecureGlobalKeys.add(Settings.Global.TETHER_DUN_REQUIRED);
- sSecureGlobalKeys.add(Settings.Global.TETHER_SUPPORTED);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_HELP_URI);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_NOTIFICATION_TYPE);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_POLLING_SEC);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_RESET_DAY);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_THRESHOLD_BYTES);
- sSecureGlobalKeys.add(Settings.Global.THROTTLE_VALUE_KBITSPS);
- sSecureGlobalKeys.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.USE_GOOGLE_MAIL);
- sSecureGlobalKeys.add(Settings.Global.WEB_AUTOFILL_QUERY_URL);
- sSecureGlobalKeys.add(Settings.Global.WIFI_COUNTRY_CODE);
- sSecureGlobalKeys.add(Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
- sSecureGlobalKeys.add(Settings.Global.WIFI_FREQUENCY_BAND);
- sSecureGlobalKeys.add(Settings.Global.WIFI_IDLE_MS);
- sSecureGlobalKeys.add(Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT);
- sSecureGlobalKeys.add(Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
- sSecureGlobalKeys.add(Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
- sSecureGlobalKeys.add(Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
- sSecureGlobalKeys.add(Settings.Global.WIFI_NUM_OPEN_NETWORKS_KEPT);
- sSecureGlobalKeys.add(Settings.Global.WIFI_ON);
- sSecureGlobalKeys.add(Settings.Global.WIFI_P2P_DEVICE_NAME);
- sSecureGlobalKeys.add(Settings.Global.WIFI_SAVED_STATE);
- sSecureGlobalKeys.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
- sSecureGlobalKeys.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.WIFI_WATCHDOG_ON);
- sSecureGlobalKeys.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
- sSecureGlobalKeys.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
- sSecureGlobalKeys.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
- sSecureGlobalKeys.add(Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
- sSecureGlobalKeys.add(Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
- sSecureGlobalKeys.add(Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
- sSecureGlobalKeys.add(Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS);
- sSecureGlobalKeys.add(Settings.Global.WTF_IS_FATAL);
- sSecureGlobalKeys.add(Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
- sSecureGlobalKeys.add(Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
- sSecureGlobalKeys.add(Settings.Global.SEND_ACTION_APP_ERROR);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_AGE_SECONDS);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_MAX_FILES);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_QUOTA_KB);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_QUOTA_PERCENT);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_RESERVE_PERCENT);
- sSecureGlobalKeys.add(Settings.Global.DROPBOX_TAG_PREFIX);
- sSecureGlobalKeys.add(Settings.Global.ERROR_LOGCAT_PREFIX);
- sSecureGlobalKeys.add(Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL);
- sSecureGlobalKeys.add(Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD);
- sSecureGlobalKeys.add(Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE);
- sSecureGlobalKeys.add(Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES);
- sSecureGlobalKeys.add(Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES);
- sSecureGlobalKeys.add(Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS);
- sSecureGlobalKeys.add(Settings.Global.CONNECTIVITY_CHANGE_DELAY);
- sSecureGlobalKeys.add(Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED);
- sSecureGlobalKeys.add(Settings.Global.CAPTIVE_PORTAL_SERVER);
- sSecureGlobalKeys.add(Settings.Global.NSD_ON);
- sSecureGlobalKeys.add(Settings.Global.SET_INSTALL_LOCATION);
- sSecureGlobalKeys.add(Settings.Global.DEFAULT_INSTALL_LOCATION);
- sSecureGlobalKeys.add(Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY);
- sSecureGlobalKeys.add(Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY);
- sSecureGlobalKeys.add(Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT);
- sSecureGlobalKeys.add(Settings.Global.HTTP_PROXY);
- sSecureGlobalKeys.add(Settings.Global.GLOBAL_HTTP_PROXY_HOST);
- sSecureGlobalKeys.add(Settings.Global.GLOBAL_HTTP_PROXY_PORT);
- sSecureGlobalKeys.add(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
- sSecureGlobalKeys.add(Settings.Global.SET_GLOBAL_HTTP_PROXY);
- sSecureGlobalKeys.add(Settings.Global.DEFAULT_DNS_SERVER);
- sSecureGlobalKeys.add(Settings.Global.PREFERRED_NETWORK_MODE);
- sSecureGlobalKeys.add(Settings.Global.PREFERRED_CDMA_SUBSCRIPTION);
+ Settings.Secure.getMovedKeys(sSecureGlobalKeys);
// Keys from the 'system' table now moved to 'global'
// These must match Settings.System.MOVED_TO_GLOBAL
sSystemGlobalKeys = new HashSet<String>();
-
- sSystemGlobalKeys.add(Settings.Global.AIRPLANE_MODE_ON);
- sSystemGlobalKeys.add(Settings.Global.AIRPLANE_MODE_RADIOS);
- sSystemGlobalKeys.add(Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
- sSystemGlobalKeys.add(Settings.Global.AUTO_TIME);
- sSystemGlobalKeys.add(Settings.Global.AUTO_TIME_ZONE);
- sSystemGlobalKeys.add(Settings.Global.CAR_DOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.CAR_UNDOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.DESK_DOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.DESK_UNDOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.DOCK_SOUNDS_ENABLED);
- sSystemGlobalKeys.add(Settings.Global.LOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.UNLOCK_SOUND);
- sSystemGlobalKeys.add(Settings.Global.LOW_BATTERY_SOUND);
- sSystemGlobalKeys.add(Settings.Global.POWER_SOUNDS_ENABLED);
- sSystemGlobalKeys.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
- sSystemGlobalKeys.add(Settings.Global.WIFI_SLEEP_POLICY);
- sSystemGlobalKeys.add(Settings.Global.MODE_RINGER);
- sSystemGlobalKeys.add(Settings.Global.WINDOW_ANIMATION_SCALE);
- sSystemGlobalKeys.add(Settings.Global.TRANSITION_ANIMATION_SCALE);
- sSystemGlobalKeys.add(Settings.Global.ANIMATOR_DURATION_SCALE);
- sSystemGlobalKeys.add(Settings.Global.FANCY_IME_ANIMATIONS);
- sSystemGlobalKeys.add(Settings.Global.COMPATIBILITY_MODE);
- sSystemGlobalKeys.add(Settings.Global.EMERGENCY_TONE);
- sSystemGlobalKeys.add(Settings.Global.CALL_AUTO_RETRY);
- sSystemGlobalKeys.add(Settings.Global.DEBUG_APP);
- sSystemGlobalKeys.add(Settings.Global.WAIT_FOR_DEBUGGER);
- sSystemGlobalKeys.add(Settings.Global.SHOW_PROCESSES);
- sSystemGlobalKeys.add(Settings.Global.ALWAYS_FINISH_ACTIVITIES);
+ Settings.System.getNonLegacyMovedKeys(sSystemGlobalKeys);
}
private boolean settingMovedToGlobal(final String name) {
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_not_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_not_connected.png
new file mode 100644
index 0000000..8fb71ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_off.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_off.png
index 7c6ca75..ac76535 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_off.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_on.png
index ff0ba07..090d235 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_on.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_not_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_not_connected.png
new file mode 100644
index 0000000..d0ce4f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_off.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_off.png
index 61eff94..2116449 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_off.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_on.png
index b480a80..1cc6e62 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_on.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_not_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_not_connected.png
new file mode 100644
index 0000000..e312f8e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_off.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_off.png
index b4d9175..44cd31b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_off.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_on.png
index 598d967..62a518a 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_on.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
index c1bcdfe..d119cf5 100644
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -22,10 +22,11 @@
android:background="@drawable/notification_panel_bg"
>
<!-- TODO: Put into ScrollView -->
- <ScrollView
+ <com.android.systemui.statusbar.phone.QuickSettingsScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/close_handle_underlap"
+ android:overScrollMode="ifContentScrolls"
>
<com.android.systemui.statusbar.phone.QuickSettingsContainerView
android:id="@+id/quick_settings_container"
@@ -34,7 +35,7 @@
android:animateLayoutChanges="true"
android:columnCount="@integer/quick_settings_num_columns"
/>
- </ScrollView>
+ </com.android.systemui.statusbar.phone.QuickSettingsScrollView>
<View
android:id="@+id/handle"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7f6098a..db25c1a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -71,7 +71,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
- android:overScrollMode="always"
+ android:overScrollMode="ifContentScrolls"
>
<com.android.systemui.statusbar.policy.NotificationRowLayout
android:id="@+id/latestItems"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index dddef6d..942e814 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -66,7 +66,7 @@
<!-- Vibration duration for MultiWaveView used in SearchPanelView -->
<integer translatable="false" name="config_search_panel_view_vibration_duration">20</integer>
- <!-- The length of the vibration when the notificaiotn pops open. -->
+ <!-- The length of the vibration when the notification pops open. -->
<integer name="one_finger_pop_duration_ms">10</integer>
<!-- Whether we're using the tablet-optimized recents interface (we use this
@@ -94,5 +94,8 @@
<!-- Timeouts for brightness dialog to disappear -->
<integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
<integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
+
+ <integer name="blinds_pop_duration_ms">10</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5023d23..4bf6c10 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -103,16 +103,16 @@
<!-- Initial velocity of the shade when collapsing on its own -->
<dimen name="self_collapse_velocity">2000dp</dimen>
<!-- Minimum final velocity of gestures interpreted as expand requests -->
- <dimen name="fling_expand_min_velocity">200dp</dimen>
+ <dimen name="fling_expand_min_velocity">100dp</dimen>
<!-- Minimum final velocity of gestures interpreted as collapse requests -->
- <dimen name="fling_collapse_min_velocity">200dp</dimen>
+ <dimen name="fling_collapse_min_velocity">100dp</dimen>
<!-- Cap on contribution of x dimension of gesture to overall velocity -->
<dimen name="fling_gesture_max_x_velocity">200dp</dimen>
<!-- Cap on overall resulting fling speed (s^-1) -->
<dimen name="fling_gesture_max_output_velocity">3000dp</dimen>
<!-- Minimum distance a fling must travel (anti-jitter) -->
- <dimen name="fling_gesture_min_dist">10dp</dimen>
+ <dimen name="fling_gesture_min_dist">20dp</dimen>
<!-- Minimum fraction of the display a gesture must travel, at any velocity, to qualify as a
collapse request -->
@@ -184,7 +184,7 @@
<!-- Height of the carrier/wifi name label -->
<dimen name="carrier_label_height">24dp</dimen>
- <!-- The distance you can pull a notificaiton before it pops open -->
+ <!-- The distance you can pull a notification before it pops open -->
<dimen name="one_finger_pop_limit">32dp</dimen>
<!-- The fixed height of each tile -->
@@ -196,4 +196,9 @@
<!-- Minimum fraction of the screen that should be taken up by the notification panel.
Not used at this screen size. -->
<item type="dimen" name="notification_panel_min_height_frac">0%</item>
+
+ <dimen name="blinds_pop_threshold">32dp</dimen>
+
+ <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
+ <dimen name="pull_span_min">25dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 674d9a3..dcfd0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -50,7 +50,7 @@
private static final long EXPAND_DURATION = 250;
private static final long GLOW_DURATION = 150;
- // Set to false to disable focus-based gestures (two-finger pull).
+ // Set to false to disable focus-based gestures (spread-finger vertical pull).
private static final boolean USE_DRAG = true;
// Set to false to disable scale-based gestures (both horizontal and vertical).
private static final boolean USE_SPAN = true;
@@ -69,8 +69,12 @@
@SuppressWarnings("unused")
private Context mContext;
- private boolean mStretching;
- private boolean mPullingWithOneFinger;
+ private boolean mExpanding;
+ private static final int NONE = 0;
+ private static final int BLINDS = 1<<0;
+ private static final int PULL = 1<<1;
+ private static final int STRETCH = 1<<2;
+ private int mExpansionStyle = NONE;
private boolean mWatchingForPull;
private boolean mHasPopped;
private View mEventSource;
@@ -86,8 +90,9 @@
private int mLastMotionY;
private float mPopLimit;
private int mPopDuration;
+ private float mPullGestureMinXSpan;
private Callback mCallback;
- private ScaleGestureDetector mDetector;
+ private ScaleGestureDetector mSGD;
private ViewScaler mScaler;
private ObjectAnimator mScaleAnimation;
private AnimatorSet mGlowAnimationSet;
@@ -122,7 +127,7 @@
if (height < 0) {
height = mView.getMeasuredHeight();
}
- return (float) height;
+ return height;
}
public int getNaturalHeight(int maximum) {
ViewGroup.LayoutParams lp = mView.getLayoutParams();
@@ -161,8 +166,9 @@
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
- mPopLimit = mContext.getResources().getDimension(R.dimen.one_finger_pop_limit);
- mPopDuration = mContext.getResources().getInteger(R.integer.one_finger_pop_duration_ms);
+ mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
+ mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
+ mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() {
@Override
@@ -193,41 +199,30 @@
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledTouchSlop();
- mDetector =
- new ScaleGestureDetector(context,
+ mSGD = new ScaleGestureDetector(context,
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()");
- float x = detector.getFocusX();
- float y = detector.getFocusY();
+ float focusX = detector.getFocusX();
+ float focusY = detector.getFocusY();
// your fingers have to be somewhat close to the bounds of the view in question
- mInitialTouchFocusY = detector.getFocusY();
+ mInitialTouchFocusY = focusY;
mInitialTouchSpan = Math.abs(detector.getCurrentSpan());
if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")");
- mStretching = initScale(findView(x, y));
- return mStretching;
+ final View underFocus = findView(focusX, focusY);
+ if (underFocus != null) {
+ startExpanding(underFocus, STRETCH);
+ }
+ return mExpanding;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView);
-
- // are we scaling or dragging?
- float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan;
- span *= USE_SPAN ? 1f : 0f;
- float drag = detector.getFocusY() - mInitialTouchFocusY;
- drag *= USE_DRAG ? 1f : 0f;
- drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
- float pull = Math.abs(drag) + Math.abs(span) + 1f;
- float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
- float target = hand + mOldHeight;
- float newHeight = clamp(target);
- mScaler.setHeight(newHeight);
-
- setGlow(calculateGlow(target, newHeight));
+ updateExpansion();
return true;
}
@@ -236,13 +231,28 @@
if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()");
// I guess we're alone now
if (DEBUG_SCALE) Slog.d(TAG, "scale end");
- finishScale(false);
+ finishExpanding(false);
clearView();
- mStretching = false;
}
});
}
+ private void updateExpansion() {
+ // are we scaling or dragging?
+ float span = Math.abs(mSGD.getCurrentSpan()) - mInitialTouchSpan;
+ span *= USE_SPAN ? 1f : 0f;
+ float drag = mSGD.getFocusY() - mInitialTouchFocusY;
+ drag *= USE_DRAG ? 1f : 0f;
+ drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
+ float pull = Math.abs(drag) + Math.abs(span) + 1f;
+ float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
+ float target = hand + mOldHeight;
+ float newHeight = clamp(target);
+ mScaler.setHeight(newHeight);
+
+ setGlow(calculateGlow(target, newHeight));
+ }
+
private float clamp(float target) {
float out = target;
out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out);
@@ -255,8 +265,8 @@
if (mEventSource != null) {
int[] location = new int[2];
mEventSource.getLocationOnScreen(location);
- x += (float) location[0];
- y += (float) location[1];
+ x += location[0];
+ y += location[1];
v = mCallback.getChildAtRawPosition(x, y);
} else {
v = mCallback.getChildAtPosition(x, y);
@@ -274,14 +284,14 @@
if (mEventSource != null) {
int[] location = new int[2];
mEventSource.getLocationOnScreen(location);
- x += (float) location[0];
- y += (float) location[1];
+ x += location[0];
+ y += location[1];
if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")");
}
int[] location = new int[2];
v.getLocationOnScreen(location);
- x -= (float) location[0];
- y -= (float) location[1];
+ x -= location[0];
+ y -= location[1];
if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")");
if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")");
boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight());
@@ -303,7 +313,7 @@
private float calculateGlow(float target, float actual) {
// glow if overscale
if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual);
- float stretch = (float) Math.abs((target - actual) / mMaximumStretch);
+ float stretch = Math.abs((target - actual) / mMaximumStretch);
float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f)));
if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength);
return (GLOW_BASE + strength * (1f - GLOW_BASE));
@@ -340,32 +350,54 @@
View.INVISIBLE : View.VISIBLE);
}
+ @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Slog.d(TAG, "interceptTouch: act=" + (ev.getAction()) +
- " stretching=" + mStretching +
- " onefinger=" + mPullingWithOneFinger);
- // check for a two-finger gesture
- mDetector.onTouchEvent(ev);
- if (mStretching) {
+ final int action = ev.getAction();
+ if (DEBUG_SCALE) Slog.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
+ " expanding=" + mExpanding +
+ (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
+ (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
+ (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
+ // check for a spread-finger vertical pull gesture
+ mSGD.onTouchEvent(ev);
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ if (mExpanding) {
return true;
} else {
- final int action = ev.getAction();
- if ((action == MotionEvent.ACTION_MOVE) && mPullingWithOneFinger) {
+ if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) {
+ // we've begun Venetian blinds style expansion
+ return true;
+ }
+ final float xspan = mSGD.getCurrentSpanX();
+ if ((action == MotionEvent.ACTION_MOVE &&
+ xspan > mPullGestureMinXSpan &&
+ xspan > mSGD.getCurrentSpanY())) {
+ // detect a vertical pulling gesture with fingers somewhat separated
+ if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
+
+ mInitialTouchFocusY = y;
+
+ final View underFocus = findView(x, y);
+ if (underFocus != null) {
+ startExpanding(underFocus, PULL);
+ }
return true;
}
if (mScrollView != null && mScrollView.getScrollY() > 0) {
return false;
}
+ // Now look for other gestures
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (mWatchingForPull) {
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
final int yDiff = y - mLastMotionY;
if (yDiff > mTouchSlop) {
+ if (DEBUG) Slog.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mLastMotionY = y;
- mPullingWithOneFinger = initScale(findView(x, y));
- if (mPullingWithOneFinger) {
+ final View underFocus = findView(x, y);
+ if (underFocus != null) {
+ startExpanding(underFocus, BLINDS);
mInitialTouchY = mLastMotionY;
mHasPopped = false;
}
@@ -375,35 +407,35 @@
}
case MotionEvent.ACTION_DOWN:
- mWatchingForPull = isInside(mScrollView, ev.getX(), ev.getY());
- mLastMotionY = (int) ev.getY();
+ mWatchingForPull = isInside(mScrollView, x, y);
+ mLastMotionY = y;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- if (mPullingWithOneFinger) {
- finishScale(false);
- clearView();
- }
- mPullingWithOneFinger = false;
- mWatchingForPull = false;
+ if (DEBUG) Slog.d(TAG, "up/cancel");
+ finishExpanding(false);
+ clearView();
break;
}
- return mPullingWithOneFinger;
+ return mExpanding;
}
}
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
- if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + (action) +
- " stretching=" + mStretching +
- " onefinger=" + mPullingWithOneFinger);
- if (mStretching) {
- mDetector.onTouchEvent(ev);
- }
+ if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
+ " expanding=" + mExpanding +
+ (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
+ (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
+ (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
+
+ mSGD.onTouchEvent(ev);
+
switch (action) {
case MotionEvent.ACTION_MOVE: {
- if (mPullingWithOneFinger) {
+ if (0 != (mExpansionStyle & BLINDS)) {
final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight;
final float newHeight = clamp(rawHeight);
final boolean wasClosed = (mOldHeight == mSmallSize);
@@ -430,57 +462,59 @@
setGlow(calculateGlow(4f * pull, 0f));
}
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- View underPointer = findView(x, y);
- if (isFinished && underPointer != null && underPointer != mCurrView) {
- finishScale(false);
- initScale(underPointer);
- mInitialTouchY = ev.getY();
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ View underFocus = findView(x, y);
+ if (isFinished && underFocus != null && underFocus != mCurrView) {
+ finishExpanding(false); // @@@ needed?
+ startExpanding(underFocus, BLINDS);
+ mInitialTouchY = y;
mHasPopped = false;
}
return true;
}
+
+ if (mExpanding) {
+ updateExpansion();
+ return true;
+ }
+
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Slog.d(TAG, "cancel");
- mStretching = false;
- if (mPullingWithOneFinger) {
- finishScale(false);
- mPullingWithOneFinger = false;
- }
+ if (DEBUG) Slog.d(TAG, "up/cancel");
+ finishExpanding(false);
clearView();
break;
}
return true;
}
- private boolean initScale(View v) {
- if (v != null) {
- if (DEBUG) Slog.d(TAG, "scale begins on view: " + v);
- mCallback.setUserLockedChild(v, true);
- setView(v);
- setGlow(GLOW_BASE);
- mScaler.setView(v);
- mOldHeight = mScaler.getHeight();
- if (mCallback.canChildBeExpanded(v)) {
- if (DEBUG) Slog.d(TAG, "working on an expandable child");
- mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
- } else {
- if (DEBUG) Slog.d(TAG, "working on a non-expandable child");
- mNaturalHeight = mOldHeight;
- }
- if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight +
- " mNaturalHeight: " + mNaturalHeight);
- v.getParent().requestDisallowInterceptTouchEvent(true);
- return true;
+
+ private void startExpanding(View v, int expandType) {
+ mExpanding = true;
+ mExpansionStyle = expandType;
+ if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v);
+ mCallback.setUserLockedChild(v, true);
+ setView(v);
+ setGlow(GLOW_BASE);
+ mScaler.setView(v);
+ mOldHeight = mScaler.getHeight();
+ if (mCallback.canChildBeExpanded(v)) {
+ if (DEBUG) Slog.d(TAG, "working on an expandable child");
+ mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
} else {
- return false;
+ if (DEBUG) Slog.d(TAG, "working on a non-expandable child");
+ mNaturalHeight = mOldHeight;
}
+ if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight +
+ " mNaturalHeight: " + mNaturalHeight);
+ v.getParent().requestDisallowInterceptTouchEvent(true);
}
- private void finishScale(boolean force) {
+ private void finishExpanding(boolean force) {
+ if (!mExpanding) return;
+
float currentHeight = mScaler.getHeight();
float targetHeight = mSmallSize;
float h = mScaler.getHeight();
@@ -501,6 +535,10 @@
mScaleAnimation.start();
}
mCallback.setUserLockedChild(mCurrView, false);
+
+ mExpanding = false;
+ mExpansionStyle = NONE;
+
if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView);
}
@@ -527,8 +565,8 @@
@Override
public void onClick(View v) {
- initScale(v);
- finishScale(true);
+ startExpanding(v, STRETCH);
+ finishExpanding(true);
clearView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index f93da08..140cc80 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -17,6 +17,7 @@
package com.android.systemui.recent;
import android.app.Activity;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,8 @@
import com.android.systemui.SystemUIApplication;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
+import java.util.List;
+
public class RecentsActivity extends Activity {
public static final String TOGGLE_RECENTS_INTENT = "com.android.systemui.TOGGLE_RECENTS";
public static final String CLOSE_RECENTS_INTENT = "com.android.systemui.CLOSE_RECENTS";
@@ -122,11 +125,15 @@
public void dismissAndGoBack() {
if (mRecentsPanel != null) {
- final SystemUIApplication app = (SystemUIApplication) getApplication();
- final RecentTasksLoader recentTasksLoader = app.getRecentTasksLoader();
- TaskDescription firstTask = mRecentsPanel.getBottomTask();
- if (firstTask != null && mRecentsPanel.simulateClick(firstTask)) {
- // recents panel will take care of calling show(false);
+ final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+
+ final List<ActivityManager.RecentTaskInfo> recentTasks =
+ am.getRecentTasks(2,
+ ActivityManager.RECENT_WITH_EXCLUDED |
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+ if (recentTasks.size() > 1 &&
+ mRecentsPanel.simulateClick(recentTasks.get(1).persistentId)) {
+ // recents panel will take care of calling show(false) through simulateClick
return;
}
mRecentsPanel.show(false);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 50b32f9..6cb7dec 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -76,11 +76,11 @@
}
}
- public View findViewForTask(TaskDescription task) {
+ public View findViewForTask(int persistentTaskId) {
for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
View v = mLinearLayout.getChildAt(i);
RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) v.getTag();
- if (holder.taskDescription == task) {
+ if (holder.taskDescription.persistentTaskId == persistentTaskId) {
return v;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 3a89059..ff51996 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -95,7 +95,7 @@
public void setAdapter(TaskDescriptionAdapter adapter);
public void setCallback(RecentsCallback callback);
public void setMinSwipeAlpha(float minAlpha);
- public View findViewForTask(TaskDescription task);
+ public View findViewForTask(int persistentTaskId);
}
private final class OnLongClickDelegate implements View.OnLongClickListener {
@@ -518,24 +518,6 @@
showIfReady();
}
- public TaskDescription getBottomTask() {
- if (mRecentsContainer != null) {
- ViewGroup container = mRecentsContainer;
- if (container instanceof RecentsScrollView) {
- container = (ViewGroup) container.findViewById(
- R.id.recents_linear_layout);
- }
- if (container.getChildCount() > 0) {
- View v = container.getChildAt(container.getChildCount() - 1);
- if (v.getTag() instanceof ViewHolder) {
- ViewHolder h = (ViewHolder)v.getTag();
- return h.taskDescription;
- }
- }
- }
- return null;
- }
-
public void onWindowAnimationStart() {
if (mItemToAnimateInWhenWindowAnimationIsFinished != null) {
final int startDelay = 100;
@@ -590,7 +572,7 @@
public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) {
mNumItemsWaitingForThumbnailsAndIcons = firstScreenful
- ? tasks.size() : mRecentTaskDescriptions == null
+ ? tasks.size() : mRecentTaskDescriptions == null
? 0 : mRecentTaskDescriptions.size();
if (mRecentTaskDescriptions == null) {
mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks);
@@ -622,11 +604,11 @@
setContentDescription(recentAppsAccessibilityDescription);
}
- public boolean simulateClick(TaskDescription task) {
+ public boolean simulateClick(int persistentTaskId) {
if (mRecentsContainer instanceof RecentsScrollView){
RecentsScrollView scrollView
= (RecentsScrollView) mRecentsContainer;
- View v = scrollView.findViewForTask(task);
+ View v = scrollView.findViewForTask(persistentTaskId);
if (v != null) {
handleOnClick(v);
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 5e0df49..47b0113 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -77,11 +77,11 @@
}
}
- public View findViewForTask(TaskDescription task) {
+ public View findViewForTask(int persistentTaskId) {
for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
View v = mLinearLayout.getChildAt(i);
RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) v.getTag();
- if (holder.taskDescription == task) {
+ if (holder.taskDescription.persistentTaskId == persistentTaskId) {
return v;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c9ec481..32b7c68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,6 +24,7 @@
import android.view.View;
import com.android.systemui.R;
+import com.android.systemui.statusbar.GestureRecorder;
public class NotificationPanelView extends PanelView {
@@ -47,9 +48,12 @@
@Override
public void fling(float vel, boolean always) {
- ((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
- "fling " + ((vel > 0) ? "open" : "closed"),
- "notifications,v=" + vel);
+ GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
+ if (gr != null) {
+ gr.tag(
+ "fling " + ((vel > 0) ? "open" : "closed"),
+ "notifications,v=" + vel);
+ }
super.fling(vel, always);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 6b9bc89..4962199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -21,14 +21,16 @@
public static final int STATE_OPENING = 1;
public static final int STATE_OPEN = 2;
- private PanelHolder mPanelHolder;
- private ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
- protected PanelView mTouchingPanel;
+ PanelHolder mPanelHolder;
+ ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
+ PanelView mTouchingPanel;
private int mState = STATE_CLOSED;
private boolean mTracking;
+ float mPanelExpandedFractionSum;
+
public void go(int state) {
- LOG("go state: %d -> %d", mState, state);
+ if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
}
@@ -84,7 +86,7 @@
if (event.getAction() == MotionEvent.ACTION_DOWN) {
final PanelView panel = selectPanelForTouchX(event.getX());
boolean enabled = panel.isEnabled();
- LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
+ if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
(enabled ? "" : " (disabled)"));
if (!enabled)
return false;
@@ -96,15 +98,21 @@
// called from PanelView when self-expanding, too
public void startOpeningPanel(PanelView panel) {
- LOG("startOpeningPanel: " + panel);
+ if (DEBUG) LOG("startOpeningPanel: " + panel);
mTouchingPanel = panel;
mPanelHolder.setSelectedPanel(mTouchingPanel);
+ for (PanelView pv : mPanels) {
+ if (pv != panel) {
+ pv.collapse();
+ }
+ }
}
public void panelExpansionChanged(PanelView panel, float frac) {
boolean fullyClosed = true;
PanelView fullyOpenedPanel = null;
- LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
+ if (DEBUG) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
+ mPanelExpandedFractionSum = 0f;
for (PanelView pv : mPanels) {
final boolean visible = pv.getVisibility() == View.VISIBLE;
// adjust any other panels that may be partially visible
@@ -115,11 +123,10 @@
}
fullyClosed = false;
final float thisFrac = pv.getExpandedFraction();
- LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);
+ mPanelExpandedFractionSum += (visible ? thisFrac : 0);
+ if (DEBUG) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);
if (panel == pv) {
if (thisFrac == 1f) fullyOpenedPanel = panel;
- } else {
- pv.setExpandedFraction(1f-frac);
}
}
if (pv.getExpandedHeight() > 0f) {
@@ -128,6 +135,7 @@
if (visible) pv.setVisibility(View.GONE);
}
}
+ mPanelExpandedFractionSum /= mPanels.size();
if (fullyOpenedPanel != null && !mTracking) {
go(STATE_OPEN);
onPanelFullyOpened(fullyOpenedPanel);
@@ -136,7 +144,7 @@
onAllPanelsCollapsed();
}
- LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
+ if (DEBUG) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
(fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
}
@@ -148,9 +156,10 @@
waiting = true;
} else {
pv.setExpandedFraction(0); // just in case
+ pv.setVisibility(View.GONE);
}
- pv.setVisibility(View.GONE);
}
+ if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
if (!waiting) {
// it's possible that nothing animated, so we replicate the termination
// conditions of panelExpansionChanged here
@@ -160,20 +169,20 @@
}
public void onPanelPeeked() {
- LOG("onPanelPeeked");
+ if (DEBUG) LOG("onPanelPeeked");
}
public void onAllPanelsCollapsed() {
- LOG("onAllPanelsCollapsed");
+ if (DEBUG) LOG("onAllPanelsCollapsed");
}
public void onPanelFullyOpened(PanelView openPanel) {
- LOG("onPanelFullyOpened");
+ if (DEBUG) LOG("onPanelFullyOpened");
}
public void onTrackingStarted(PanelView panel) {
mTracking = true;
- if (panel != mTouchingPanel) {
+ if (DEBUG && panel != mTouchingPanel) {
LOG("shouldn't happen: onTrackingStarted(%s) != mTouchingPanel(%s)",
panel, mTouchingPanel);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
index abd82bd..241ac3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
@@ -7,7 +7,7 @@
public class PanelHolder extends FrameLayout {
- private int mSelectedPanelIndex;
+ private int mSelectedPanelIndex = -1;
private PanelBar mBar;
public PanelHolder(Context context, AttributeSet attrs) {
@@ -53,6 +53,7 @@
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ PanelBar.LOG("PanelHolder got touch in open air, closing panels");
mBar.collapseAllPanels(true);
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index d94dbe4..ca1f75c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -9,14 +9,9 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
public class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
@@ -70,12 +65,16 @@
}
};
- private final Runnable mStopAnimator = new Runnable() { public void run() {
- if (mTimeAnimator.isStarted()) {
- mTimeAnimator.end();
- mRubberbanding = false;
+ private final Runnable mStopAnimator = new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeAnimator.isStarted()) {
+ mTimeAnimator.end();
+ mRubberbanding = false;
+ mClosing = false;
+ }
}
- }};
+ };
private float mVel, mAccel;
private int mFullHeight = 0;
@@ -90,20 +89,22 @@
mTimeAnimator.setTimeListener(mAnimationCallback);
mTimeAnimator.start();
-
- mRubberbanding = STRETCH_PAST_CONTENTS && mExpandedHeight > getFullHeight();
+
+ mRubberbanding = STRETCH_PAST_CONTENTS // is it enabled at all?
+ && mExpandedHeight > getFullHeight() // are we past the end?
+ && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
if (mRubberbanding) {
mClosing = true;
} else if (mVel == 0) {
- // if the panel is less than halfway open, close it
+ // if the panel is less than halfway open, close it
mClosing = (mFinalTouchY / getFullHeight()) < 0.5f;
} else {
mClosing = mExpandedHeight > 0 && mVel < 0;
}
} else if (dtms > 0) {
final float dt = dtms * 0.001f; // ms -> s
- LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
- LOG("tick: before: h=%d", (int) mExpandedHeight);
+ if (DEBUG) LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
+ if (DEBUG) LOG("tick: before: h=%d", (int) mExpandedHeight);
final float fh = getFullHeight();
boolean braking = false;
@@ -136,12 +137,12 @@
}
float h = mExpandedHeight + mVel * dt;
-
+
if (mRubberbanding && h < fh) {
h = fh;
}
- LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
+ if (DEBUG) LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
setExpandedHeightInternal(h);
@@ -205,14 +206,14 @@
loadDimens();
mHandleView = findViewById(R.id.handle);
- LOG("handle view: " + mHandleView);
+ if (DEBUG) LOG("handle view: " + mHandleView);
if (mHandleView != null) {
mHandleView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final float y = event.getY();
final float rawY = event.getRawY();
- LOG("handle.onTouch: a=%s y=%.1f rawY=%.1f off=%.1f",
+ if (DEBUG) LOG("handle.onTouch: a=%s y=%.1f rawY=%.1f off=%.1f",
MotionEvent.actionToString(event.getAction()),
y, rawY, mTouchOffset);
PanelView.this.getLocationOnScreen(mAbsPos);
@@ -224,6 +225,7 @@
mInitialTouchY = y;
mVelocityTracker = VelocityTracker.obtain();
trackMovement(event);
+ mTimeAnimator.cancel(); // end any outstanding animations
mBar.onTrackingStarted(PanelView.this);
mTouchOffset = (rawY - mAbsPos[1]) - PanelView.this.getExpandedHeight();
break;
@@ -263,9 +265,9 @@
// if you've barely moved your finger, we treat the velocity as 0
// preventing spurious flings due to touch screen jitter
- final float deltaY = (float)Math.abs(mFinalTouchY - mInitialTouchY);
+ final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
if (deltaY < mFlingGestureMinDistPx
- || vel < mFlingGestureMinDistPx) {
+ || vel < mFlingExpandMinVelocityPx) {
vel = 0;
}
@@ -273,7 +275,7 @@
vel = -vel;
}
- LOG("gesture: dy=%f vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
+ if (DEBUG) LOG("gesture: dy=%f vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
deltaY,
mVelocityTracker.getXVelocity(),
mVelocityTracker.getYVelocity(),
@@ -312,7 +314,7 @@
@Override
protected void onViewAdded(View child) {
- LOG("onViewAdded: " + child);
+ if (DEBUG) LOG("onViewAdded: " + child);
}
public View getHandle() {
@@ -324,7 +326,7 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- LOG("onMeasure(%d, %d) -> (%d, %d)",
+ if (DEBUG) LOG("onMeasure(%d, %d) -> (%d, %d)",
widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
// Did one of our children change size?
@@ -332,7 +334,7 @@
if (newHeight != mFullHeight) {
mFullHeight = newHeight;
// If the user isn't actively poking us, let's rubberband to the content
- if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
+ if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
&& mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
mExpandedHeight = mFullHeight;
}
@@ -351,7 +353,7 @@
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
- LOG("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, (int)mFullHeight);
+ if (DEBUG) LOG("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, mFullHeight);
super.onLayout(changed, left, top, right, bottom);
}
@@ -365,7 +367,7 @@
if (!(STRETCH_PAST_CONTENTS && (mTracking || mRubberbanding)) && h > fh) h = fh;
mExpandedHeight = h;
- LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
+ if (DEBUG) LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
requestLayout();
// FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
@@ -377,9 +379,9 @@
private float getFullHeight() {
if (mFullHeight <= 0) {
- LOG("Forcing measure() since fullHeight=" + mFullHeight);
- measure(MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
+ if (DEBUG) LOG("Forcing measure() since fullHeight=" + mFullHeight);
+ measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
}
return mFullHeight;
}
@@ -397,11 +399,15 @@
}
public boolean isFullyExpanded() {
- return mExpandedHeight == getFullHeight();
+ return mExpandedHeight >= getFullHeight();
}
public boolean isFullyCollapsed() {
- return mExpandedHeight == 0;
+ return mExpandedHeight <= 0;
+ }
+
+ public boolean isCollapsing() {
+ return mClosing;
}
public void setBar(PanelBar panelBar) {
@@ -411,6 +417,8 @@
public void collapse() {
// TODO: abort animation or ongoing touch
if (!isFullyCollapsed()) {
+ // collapse() should never be a rubberband, even if an animation is already running
+ mRubberbanding = false;
fling(-mSelfCollapseVelocityPx, /*always=*/ true);
}
}
@@ -418,10 +426,10 @@
public void expand() {
if (isFullyCollapsed()) {
mBar.startOpeningPanel(this);
- LOG("expand: calling fling(%s, true)", mSelfExpandVelocityPx);
+ if (DEBUG) LOG("expand: calling fling(%s, true)", mSelfExpandVelocityPx);
fling (mSelfExpandVelocityPx, /*always=*/ true);
} else if (DEBUG) {
- LOG("skipping expansion: is expanded");
+ if (DEBUG) LOG("skipping expansion: is expanded");
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 493a92a..a12af8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -99,6 +99,7 @@
public static final boolean DEBUG = BaseStatusBar.DEBUG;
public static final boolean SPEW = DEBUG;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
+ public static final boolean DEBUG_GESTURES = false;
// additional instrumentation for testing purposes; intended to be left on during development
public static final boolean CHATTY = DEBUG;
@@ -247,7 +248,9 @@
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
// XXX: gesture research
- private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat");
+ private final GestureRecorder mGestureRec = DEBUG_GESTURES
+ ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
+ : null;
private int mNavigationIconHints = 0;
private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
@@ -1350,7 +1353,9 @@
}
}
- mGestureRec.add(event);
+ if (DEBUG_GESTURES) {
+ mGestureRec.add(event);
+ }
return false;
}
@@ -1630,8 +1635,10 @@
}
}
- pw.print(" status bar gestures: ");
- mGestureRec.dump(fd, pw, args);
+ if (DEBUG_GESTURES) {
+ pw.print(" status bar gestures: ");
+ mGestureRec.dump(fd, pw, args);
+ }
mNetworkController.dump(fd, pw, args);
}
@@ -1713,8 +1720,10 @@
// called by makeStatusbar and also by PhoneStatusBarView
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
- mGestureRec.tag("display",
- String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ if (DEBUG_GESTURES) {
+ mGestureRec.tag("display",
+ String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
}
private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 6517e7c..15ac5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -19,27 +19,14 @@
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Slog;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-
import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.policy.FixedSizeDrawable;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
@@ -53,6 +40,7 @@
boolean mFullWidthNotifications;
PanelView mFadingPanel = null;
PanelView mNotificationPanel, mSettingsPanel;
+ private boolean mShouldFade;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -112,7 +100,7 @@
if (DEBUG) {
Slog.v(TAG, "notif frac=" + mNotificationPanel.getExpandedFraction());
}
- return (mNotificationPanel.getExpandedFraction() == 1.0f)
+ return (mNotificationPanel.getExpandedFraction() > 0f)
? mSettingsPanel : mNotificationPanel;
}
@@ -120,7 +108,7 @@
// right 1/3 for quick settings. If you pull the status bar down a second time you'll
// toggle panels no matter where you pull it down.
- final float w = (float) getMeasuredWidth();
+ final float w = getMeasuredWidth();
float region = (w * mSettingsPanelDragzoneFrac);
if (DEBUG) {
@@ -138,9 +126,18 @@
public void onPanelPeeked() {
super.onPanelPeeked();
mBar.makeExpandedVisible(true);
- if (mFadingPanel == null) {
- mFadingPanel = mTouchingPanel;
+ }
+
+ @Override
+ public void startOpeningPanel(PanelView panel) {
+ super.startOpeningPanel(panel);
+ // we only want to start fading if this is the "first" or "last" panel,
+ // which is kind of tricky to determine
+ mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
+ if (DEBUG) {
+ Slog.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
}
+ mFadingPanel = panel;
}
@Override
@@ -153,6 +150,7 @@
@Override
public void onPanelFullyOpened(PanelView openPanel) {
mFadingPanel = openPanel;
+ mShouldFade = true; // now you own the fade, mister
}
@Override
@@ -166,24 +164,24 @@
}
@Override
- public void panelExpansionChanged(PanelView pv, float frac) {
- super.panelExpansionChanged(pv, frac);
+ public void panelExpansionChanged(PanelView panel, float frac) {
+ super.panelExpansionChanged(panel, frac);
if (DEBUG) {
Slog.v(TAG, "panelExpansionChanged: f=" + frac);
}
- if (mFadingPanel == pv
- && mScrimColor != 0 && ActivityManager.isHighEndGfx()) {
- // woo, special effects
- final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2.2f))));
- // attenuate background color alpha by k
- final int color = (int) ((float)(mScrimColor >>> 24) * k) << 24 | (mScrimColor & 0xFFFFFF);
- mBar.mStatusBarWindow.setBackgroundColor(color);
+ if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()) {
+ if (mShouldFade) {
+ frac = mPanelExpandedFractionSum; // don't judge me
+ // woo, special effects
+ final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2.2f))));
+ // attenuate background color alpha by k
+ final int color = (int) ((mScrimColor >>> 24) * k) << 24 | (mScrimColor & 0xFFFFFF);
+ mBar.mStatusBarWindow.setBackgroundColor(color);
+ }
}
mBar.updateCarrierLabelVisibility(false);
}
-
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index c31e138..4ef35aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -81,6 +82,7 @@
private DisplayManager mDisplayManager;
private WifiDisplayStatus mWifiDisplayStatus;
private PhoneStatusBar mStatusBarService;
+ private QuickSettingsModel.BluetoothState mBluetoothState;
private BrightnessController mBrightnessController;
private BluetoothController mBluetoothController;
@@ -115,6 +117,7 @@
mContainerView = container;
mModel = new QuickSettingsModel(context);
mWifiDisplayStatus = new WifiDisplayStatus();
+ mBluetoothState = new QuickSettingsModel.BluetoothState();
mHandler = new Handler();
Resources r = mContext.getResources();
@@ -128,6 +131,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
}
@@ -739,6 +743,10 @@
mModel.onWifiDisplayStateChanged(mWifiDisplayStatus);
}
+ private void applyBluetoothStatus() {
+ mModel.onBluetoothStateChange(mBluetoothState);
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -748,6 +756,12 @@
mWifiDisplayStatus = status;
applyWifiDisplayStatus();
}
+ if (intent.getAction().equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+ int status = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ mBluetoothState.connected = (status == BluetoothAdapter.STATE_CONNECTED);
+ applyBluetoothStatus();
+ }
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 6b9a321..0e53617 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -77,6 +77,9 @@
static class BrightnessState extends State {
boolean autoBrightness;
}
+ public static class BluetoothState extends State {
+ boolean connected = false;
+ }
/** The callback to update a given tile. */
interface RefreshCallback {
@@ -173,7 +176,7 @@
private QuickSettingsTileView mBluetoothTile;
private RefreshCallback mBluetoothCallback;
- private State mBluetoothState = new State();
+ private BluetoothState mBluetoothState = new BluetoothState();
private QuickSettingsTileView mBatteryTile;
private RefreshCallback mBatteryCallback;
@@ -354,7 +357,7 @@
mWifiState.label = removeDoubleQuotes(enabledDesc);
} else if (wifiNotConnected) {
mWifiState.iconId = R.drawable.ic_qs_wifi_0;
- mWifiState.label = r.getString(R.string.quick_settings_wifi_not_connected);
+ mWifiState.label = r.getString(R.string.quick_settings_wifi_label);
} else {
mWifiState.iconId = R.drawable.ic_qs_wifi_no_network;
mWifiState.label = r.getString(R.string.quick_settings_wifi_off_label);
@@ -398,7 +401,10 @@
mBluetoothCallback = cb;
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- onBluetoothStateChange(adapter.isEnabled());
+ mBluetoothState.enabled = adapter.isEnabled();
+ mBluetoothState.connected =
+ (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED);
+ onBluetoothStateChange(mBluetoothState);
}
boolean deviceSupportsBluetooth() {
return (BluetoothAdapter.getDefaultAdapter() != null);
@@ -406,11 +412,20 @@
// BluetoothController callback
@Override
public void onBluetoothStateChange(boolean on) {
+ mBluetoothState.enabled = on;
+ onBluetoothStateChange(mBluetoothState);
+ }
+ public void onBluetoothStateChange(BluetoothState bluetoothStateIn) {
// TODO: If view is in awaiting state, disable
Resources r = mContext.getResources();
- mBluetoothState.enabled = on;
- if (on) {
- mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on;
+ mBluetoothState.enabled = bluetoothStateIn.enabled;
+ mBluetoothState.connected = bluetoothStateIn.connected;
+ if (mBluetoothState.enabled) {
+ if (mBluetoothState.connected) {
+ mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on;
+ } else {
+ mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_not_connected;
+ }
mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_label);
} else {
mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off;
@@ -632,5 +647,4 @@
onNextAlarmChanged();
onBugreportChanged();
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java
new file mode 100644
index 0000000..8a2f8d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 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.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ScrollView;
+
+public class QuickSettingsScrollView extends ScrollView {
+
+ public QuickSettingsScrollView(Context context) {
+ super(context);
+ }
+
+ public QuickSettingsScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QuickSettingsScrollView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ // Y U NO PROTECTED
+ private int getScrollRange() {
+ int scrollRange = 0;
+ if (getChildCount() > 0) {
+ View child = getChildAt(0);
+ scrollRange = Math.max(0,
+ child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+ }
+ return scrollRange;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ final int range = getScrollRange();
+ if (range == 0) {
+ return false;
+ }
+
+ return super.onTouchEvent(ev);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
index f9d9dac0..e555277 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -29,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -95,9 +96,12 @@
@Override
public void fling(float vel, boolean always) {
- ((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
- "fling " + ((vel > 0) ? "open" : "closed"),
- "settings,v=" + vel);
+ GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
+ if (gr != null) {
+ gr.tag(
+ "fling " + ((vel > 0) ? "open" : "closed"),
+ "settings,v=" + vel);
+ }
super.fling(vel, always);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index 5007cf4..ff06630 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -26,6 +26,7 @@
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.app.AlertActivity;
@@ -90,7 +91,7 @@
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- startActivity(intent);
+ startActivityAsUser(intent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "startActivity failed for " + mUri);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 030a261..3eccccd 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -16,23 +16,21 @@
package com.android.systemui.usb;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
-import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,7 +40,6 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-
import com.android.systemui.R;
public class UsbConfirmActivity extends AlertActivity
@@ -62,10 +59,10 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Intent intent = getIntent();
- mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ Intent intent = getIntent();
+ mDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
- mResolveInfo = (ResolveInfo)intent.getParcelableExtra("rinfo");
+ mResolveInfo = (ResolveInfo) intent.getParcelableExtra("rinfo");
PackageManager packageManager = getPackageManager();
String appName = mResolveInfo.loadLabel(packageManager).toString();
@@ -117,7 +114,8 @@
try {
IBinder b = ServiceManager.getService(USB_SERVICE);
IUsbManager service = IUsbManager.Stub.asInterface(b);
- int uid = mResolveInfo.activityInfo.applicationInfo.uid;
+ final int uid = mResolveInfo.activityInfo.applicationInfo.uid;
+ final int userId = UserHandle.myUserId();
boolean alwaysUse = mAlwaysUse.isChecked();
Intent intent = null;
@@ -129,9 +127,10 @@
service.grantDevicePermission(mDevice, uid);
// set or clear default setting
if (alwaysUse) {
- service.setDevicePackage(mDevice, mResolveInfo.activityInfo.packageName);
+ service.setDevicePackage(
+ mDevice, mResolveInfo.activityInfo.packageName, userId);
} else {
- service.setDevicePackage(mDevice, null);
+ service.setDevicePackage(mDevice, null, userId);
}
} else if (mAccessory != null) {
intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
@@ -141,10 +140,10 @@
service.grantAccessoryPermission(mAccessory, uid);
// set or clear default setting
if (alwaysUse) {
- service.setAccessoryPackage(mAccessory,
- mResolveInfo.activityInfo.packageName);
+ service.setAccessoryPackage(
+ mAccessory, mResolveInfo.activityInfo.packageName, userId);
} else {
- service.setAccessoryPackage(mAccessory, null);
+ service.setAccessoryPackage(mAccessory, null, userId);
}
}
@@ -152,7 +151,7 @@
intent.setComponent(
new ComponentName(mResolveInfo.activityInfo.packageName,
mResolveInfo.activityInfo.name));
- startActivity(intent);
+ startActivityAsUser(intent, new UserHandle(userId));
} catch (Exception e) {
Log.e(TAG, "Unable to start activity", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index c384f50..6e88d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -67,7 +68,7 @@
mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
- mUid = intent.getIntExtra("uid", 0);
+ mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
mPackageName = intent.getStringExtra("package");
PackageManager packageManager = getPackageManager();
@@ -128,7 +129,8 @@
if (mPermissionGranted) {
service.grantDevicePermission(mDevice, mUid);
if (mAlwaysUse.isChecked()) {
- service.setDevicePackage(mDevice, mPackageName);
+ final int userId = UserHandle.getUserId(mUid);
+ service.setDevicePackage(mDevice, mPackageName, userId);
}
}
}
@@ -137,7 +139,8 @@
if (mPermissionGranted) {
service.grantAccessoryPermission(mAccessory, mUid);
if (mAlwaysUse.isChecked()) {
- service.setAccessoryPackage(mAccessory, mPackageName);
+ final int userId = UserHandle.getUserId(mUid);
+ service.setAccessoryPackage(mAccessory, mPackageName, userId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index f61ecb1..9928f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -16,8 +16,6 @@
package com.android.systemui.usb;
-import com.android.internal.app.ResolverActivity;
-
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ResolveInfo;
@@ -30,9 +28,11 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.widget.CheckBox;
+import com.android.internal.app.ResolverActivity;
import com.android.systemui.R;
import java.util.ArrayList;
@@ -92,34 +92,36 @@
super.onDestroy();
}
+ @Override
protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
try {
IBinder b = ServiceManager.getService(USB_SERVICE);
IUsbManager service = IUsbManager.Stub.asInterface(b);
- int uid = ri.activityInfo.applicationInfo.uid;
+ final int uid = ri.activityInfo.applicationInfo.uid;
+ final int userId = UserHandle.myUserId();
if (mDevice != null) {
// grant permission for the device
service.grantDevicePermission(mDevice, uid);
// set or clear default setting
if (alwaysCheck) {
- service.setDevicePackage(mDevice, ri.activityInfo.packageName);
+ service.setDevicePackage(mDevice, ri.activityInfo.packageName, userId);
} else {
- service.setDevicePackage(mDevice, null);
+ service.setDevicePackage(mDevice, null, userId);
}
} else if (mAccessory != null) {
// grant permission for the accessory
service.grantAccessoryPermission(mAccessory, uid);
// set or clear default setting
if (alwaysCheck) {
- service.setAccessoryPackage(mAccessory, ri.activityInfo.packageName);
+ service.setAccessoryPackage(mAccessory, ri.activityInfo.packageName, userId);
} else {
- service.setAccessoryPackage(mAccessory, null);
+ service.setAccessoryPackage(mAccessory, null, userId);
}
}
try {
- startActivity(intent);
+ startActivityAsUser(intent, new UserHandle(userId));
} catch (ActivityNotFoundException e) {
Log.e(TAG, "startActivity failed", e);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 93f2aa5..ba76bcd7 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2573,11 +2573,13 @@
final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
+ final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
+
if (!isDefaultDisplay) {
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf);
} else {
// Give the window full screen.
pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
@@ -2596,8 +2598,6 @@
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
} else {
- final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
-
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)
&& (sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
@@ -2611,7 +2611,7 @@
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf);
} else {
if (attrs.type == TYPE_STATUS_BAR_PANEL
|| attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
@@ -2826,7 +2826,8 @@
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
- if (attrs.type == TYPE_INPUT_METHOD && !win.getGivenInsetsPendingLw()) {
+ if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
+ && !win.getGivenInsetsPendingLw()) {
setLastInputMethodWindowLw(null, null);
offsetInputMethodWindowLw(win);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index a7fc1a1..f0dfba1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -67,6 +67,8 @@
private AppWidgetHost mAppWidgetHost;
private KeyguardWidgetPager mAppWidgetContainer;
private ViewFlipper mSecurityViewContainer;
+ private KeyguardSelectorView mKeyguardSelectorView;
+ private KeyguardTransportControlView mTransportControl;
private boolean mEnableMenuKey;
private boolean mIsVerifyUnlockOnly;
private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
@@ -80,7 +82,6 @@
private KeyguardSecurityModel mSecurityModel;
private Rect mTempRect = new Rect();
- private KeyguardTransportControlView mTransportControl;
/*package*/ interface TransportCallback {
void hide();
@@ -90,6 +91,7 @@
/*package*/ interface UserSwitcherCallback {
void hideSecurityView(int duration);
void showSecurityView();
+ void showUnlockHint();
}
public KeyguardHostView(Context context) {
@@ -144,46 +146,9 @@
KeyguardWidgetRegion kgwr = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
kgwr.setVisibility(VISIBLE);
mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
+ mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
- // This code manages showing/hiding the transport control. We keep it around and only
- // add it to the hierarchy if it needs to be present.
- mTransportControl =
- (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
- if (mTransportControl != null) {
- mTransportControl.setKeyguardCallback(new TransportCallback() {
- boolean mSticky = false;
- @Override
- public void hide() {
- int page = getWidgetPosition(R.id.keyguard_transport_control);
- if (page != -1 && !mSticky) {
- if (page == mAppWidgetContainer.getCurrentPage()) {
- // Switch back to clock view if music was showing.
- mAppWidgetContainer
- .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
- }
- mAppWidgetContainer.removeView(mTransportControl);
- // XXX keep view attached to hierarchy so we still get show/hide events
- // from AudioManager
- KeyguardHostView.this.addView(mTransportControl);
- mTransportControl.setVisibility(View.GONE);
- showAppropriateWidgetPage();
- }
- }
-
- @Override
- public void show() {
- if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
- KeyguardHostView.this.removeView(mTransportControl);
- mAppWidgetContainer.addView(mTransportControl,
- getWidgetPosition(R.id.keyguard_status_view) + 1);
- mTransportControl.setVisibility(View.VISIBLE);
- // Once shown, leave it showing
- mSticky = true;
- showAppropriateWidgetPage();
- }
- }
- });
- }
+ addDefaultWidgets();
updateSecurityViews();
setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
}
@@ -411,6 +376,18 @@
showSecurityScreen(mSecurityModel.getBackupFor(currentMode));
}
+ public boolean showNextSecurityScreenIfPresent() {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ if (SecurityMode.None == securityMode) {
+ return false;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ return true;
+ }
+ }
+
private void showNextSecurityScreenOrFinish(boolean authenticated) {
boolean finish = false;
if (SecurityMode.None == mCurrentSecuritySelection) {
@@ -702,6 +679,52 @@
}
}
+ private void addDefaultWidgets() {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
+ inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true);
+
+ mTransportControl =
+ (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
+
+
+ // This code manages showing/hiding the transport control. We keep it around and only
+ // add it to the hierarchy if it needs to be present.
+ if (mTransportControl != null) {
+ mTransportControl.setKeyguardCallback(new TransportCallback() {
+ boolean mSticky = false;
+ @Override
+ public void hide() {
+ int page = getWidgetPosition(R.id.keyguard_transport_control);
+ if (page != -1 && !mSticky) {
+ if (page == mAppWidgetContainer.getCurrentPage()) {
+ // Switch back to clock view if music was showing.
+ mAppWidgetContainer
+ .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
+ }
+ mAppWidgetContainer.removeView(mTransportControl);
+ // XXX keep view attached to hierarchy so we still get show/hide events
+ // from AudioManager
+ KeyguardHostView.this.addView(mTransportControl);
+ mTransportControl.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void show() {
+ if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
+ KeyguardHostView.this.removeView(mTransportControl);
+ mAppWidgetContainer.addView(mTransportControl,
+ getWidgetPosition(R.id.keyguard_status_view) + 1);
+ mTransportControl.setVisibility(View.VISIBLE);
+ // Once shown, leave it showing
+ mSticky = true;
+ }
+ }
+ });
+ }
+ }
+
private void maybePopulateWidgets() {
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
@@ -716,6 +739,7 @@
inflateAndAddUserSelectorWidgetIfNecessary();
// Add status widget
+ View statusView = null;
int statusWidgetId = mLockPatternUtils.getStatusWidget();
if (statusWidgetId != -1) {
addWidget(statusWidgetId);
@@ -729,6 +753,16 @@
mAppWidgetContainer.removeView(newStatusWidget);
newStatusWidget.setId(R.id.keyguard_status_view);
mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
+ statusView = newStatusWidget;
+ } else {
+ statusView = findViewById(R.id.keyguard_status_view);
+ }
+
+ // Disable all user interaction on status view. This is done to prevent falsing in the
+ // pocket from triggering useractivity and prevents 3rd party replacement widgets
+ // from responding to user interaction while in this position.
+ if (statusView instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
}
// Add user-selected widget
@@ -775,6 +809,12 @@
public void showSecurityView() {
mSecurityViewContainer.setAlpha(1.0f);
}
+
+ @Override
+ public void showUnlockHint() {
+ mKeyguardSelectorView.ping();
+ }
+
};
multiUser.setCallback(callback);
}
@@ -812,4 +852,8 @@
}
}
+ public void goToUserSwitcher() {
+ mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
+ }
+
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 8c1dfe1..7266883 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -22,11 +22,11 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -38,9 +38,13 @@
private ImageView mUserImage;
private TextView mUserName;
private UserInfo mUserInfo;
- private static final int INACTIVE_COLOR = 85;
- private static final int INACTIVE_ALPHA = 195;
- private static final float ACTIVE_SCALE = 1.1f;
+ private static final float ACTIVE_ALPHA = 1.0f;
+ private static final float INACTIVE_ALPHA = 0.5f;
+ private static final float ACTIVE_SCALE = 1.2f;
+ private static final float ACTIVE_TEXT_BACGROUND_ALPHA = 0.5f;
+ private static final float INACTIVE_TEXT_BACGROUND_ALPHA = 0f;
+ private static int mActiveTextColor;
+ private static int mInactiveTextColor;
private boolean mActive;
private boolean mInit = true;
private KeyguardMultiUserSelectorView mUserSelector;
@@ -53,31 +57,32 @@
KeyguardMultiUserAvatar icon = (KeyguardMultiUserAvatar)
LayoutInflater.from(context).inflate(resId, userSelector, false);
- icon.setup(info, userSelector);
+ icon.init(info, userSelector);
return icon;
}
public KeyguardMultiUserAvatar(Context context) {
- super(context, null, 0);
+ this(context, null, 0);
}
public KeyguardMultiUserAvatar(Context context, AttributeSet attrs) {
- super(context, attrs, 0);
+ this(context, attrs, 0);
}
public KeyguardMultiUserAvatar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ Resources res = mContext.getResources();
+ mActiveTextColor = res.getColor(R.color.kg_multi_user_text_active);
+ mInactiveTextColor = res.getColor(R.color.kg_multi_user_text_inactive);
}
- public void setup(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
+ public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
mUserInfo = user;
mUserSelector = userSelector;
- init();
- }
- private void init() {
mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
- mUserName = (TextView) findViewById(R.id.keyguard_user_name);
+ mUserName = (TextView) findViewById(R.id.keyguard_user_name);
mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
mUserName.setText(mUserInfo.name);
@@ -89,51 +94,61 @@
public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
if (mActive != active || mInit) {
mActive = active;
- final int finalFilterAlpha = mActive ? 0 : INACTIVE_ALPHA;
- final int initFilterAlpha = mActive ? INACTIVE_ALPHA : 0;
-
- final float finalScale = mActive ? ACTIVE_SCALE : 1.0f;
- final float initScale = mActive ? 1.0f : ACTIVE_SCALE;
if (active) {
KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
parent.setTopChild(parent.indexOfChild(this));
}
+ }
+ updateVisualsForActive(mActive, animate, duration, true, onComplete);
+ }
- if (animate) {
- ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
- va.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float r = animation.getAnimatedFraction();
- float scale = (1 - r) * initScale + r * finalScale;
- int filterAlpha = (int) ((1 - r) * initFilterAlpha + r * finalFilterAlpha);
- setScaleX(scale);
- setScaleY(scale);
- mUserImage.setColorFilter(Color.argb(filterAlpha, INACTIVE_COLOR,
- INACTIVE_COLOR, INACTIVE_COLOR));
- mUserSelector.invalidate();
+ void updateVisualsForActive(boolean active, boolean animate, int duration, boolean scale,
+ final Runnable onComplete) {
+ final float finalAlpha = active ? ACTIVE_ALPHA : INACTIVE_ALPHA;
+ final float initAlpha = active ? INACTIVE_ALPHA : ACTIVE_ALPHA;
+ final float finalScale = active && scale ? ACTIVE_SCALE : 1.0f;
+ final float initScale = active ? 1.0f : ACTIVE_SCALE;
+ final int finalTextBgAlpha = active ? (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255) :
+ (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255);
+ final int initTextBgAlpha = active ? (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255) :
+ (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255);
+ int textColor = active ? mActiveTextColor : mInactiveTextColor;
+ mUserName.setTextColor(textColor);
- }
- });
- va.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (onComplete != null) {
- onComplete.run();
- }
- }
- });
- va.setDuration(duration);
- va.start();
- } else {
- setScaleX(finalScale);
- setScaleY(finalScale);
- mUserImage.setColorFilter(Color.argb(finalFilterAlpha, INACTIVE_COLOR,
- INACTIVE_COLOR, INACTIVE_COLOR));
- if (onComplete != null) {
- post(onComplete);
+ if (animate) {
+ ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ va.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float r = animation.getAnimatedFraction();
+ float scale = (1 - r) * initScale + r * finalScale;
+ float alpha = (1 - r) * initAlpha + r * finalAlpha;
+ int textBgAlpha = (int) ((1 - r) * initTextBgAlpha + r * finalTextBgAlpha);
+ setScaleX(scale);
+ setScaleY(scale);
+ mUserImage.setAlpha(alpha);
+ mUserName.setBackgroundColor(Color.argb(textBgAlpha, 0, 0, 0));
+ mUserSelector.invalidate();
}
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+ });
+ va.setDuration(duration);
+ va.start();
+ } else {
+ setScaleX(finalScale);
+ setScaleY(finalScale);
+ mUserImage.setAlpha(finalAlpha);
+ mUserName.setBackgroundColor(Color.argb(finalTextBgAlpha, 0, 0, 0));
+ if (onComplete != null) {
+ post(onComplete);
}
}
}
@@ -156,13 +171,7 @@
public void setPressed(boolean pressed) {
if (!mPressedStateLocked) {
super.setPressed(pressed);
- if (pressed) {
- mUserImage.setColorFilter(Color.argb(0, INACTIVE_COLOR,
- INACTIVE_COLOR, INACTIVE_COLOR));
- } else if (!mActive) {
- mUserImage.setColorFilter(Color.argb(INACTIVE_ALPHA, INACTIVE_COLOR,
- INACTIVE_COLOR, INACTIVE_COLOR));
- }
+ updateVisualsForActive(pressed || mActive, false, 0, mActive, null);
} else {
mTempPressedStateHolder = pressed;
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index 3b45c22..8214142 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -107,7 +107,8 @@
if (!(v instanceof KeyguardMultiUserAvatar)) return;
final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
if (mActiveUserAvatar == avatar) {
- // They clicked the active user, no need to do anything
+ // If they click the currently active user, show the unlock hint
+ mCallback.showUnlockHint();
return;
} else {
// Reset the previously active user to appear inactive
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index 4003754..e3b7b01 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -179,6 +179,10 @@
return mGlowPadView.getTargetPosition(resId) != -1;
}
+ public void ping() {
+ mGlowPadView.ping();
+ }
+
private void updateTargets() {
int currentUserHandle = mLockPatternUtils.getCurrentUser();
DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index a3a9c5f..23a96fb 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -24,9 +24,11 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,6 +36,7 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
+import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.R;
@@ -89,7 +92,7 @@
boolean enableScreenRotation = shouldEnableScreenRotation();
- maybeCreateKeyguardLocked(enableScreenRotation);
+ maybeCreateKeyguardLocked(enableScreenRotation, false);
maybeEnableScreenRotation(enableScreenRotation);
// Disable common aspects of the system/status/navigation bars that are not appropriate or
@@ -120,13 +123,19 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- maybeCreateKeyguardLocked(shouldEnableScreenRotation());
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), false);
}
}
- private void maybeCreateKeyguardLocked(boolean enableScreenRotation) {
+ SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
+
+ private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean userSwitched) {
final boolean isActivity = (mContext instanceof Activity); // for test activity
+ if (mKeyguardHost != null) {
+ mKeyguardHost.saveHierarchyState(mStateContainer);
+ }
+
if (mKeyguardHost == null) {
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
@@ -161,11 +170,13 @@
mWindowLayoutParams = lp;
mViewManager.addView(mKeyguardHost, lp);
}
- inflateKeyguardView();
+ inflateKeyguardView(userSwitched);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+
+ mKeyguardHost.restoreHierarchyState(mStateContainer);
}
- private void inflateKeyguardView() {
+ private void inflateKeyguardView(boolean userSwitched) {
View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
if (v != null) {
mKeyguardHost.removeView(v);
@@ -179,6 +190,11 @@
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
+ if (userSwitched) {
+ mKeyguardView.goToUserSwitcher();
+ mKeyguardView.showNextSecurityScreenIfPresent();
+ }
+
if (mScreenOn) {
mKeyguardView.show();
}
@@ -219,11 +235,11 @@
/**
* Reset the state of the view.
*/
- public synchronized void reset() {
+ public synchronized void reset(boolean userSwitched) {
if (DEBUG) Log.d(TAG, "reset()");
// User might have switched, check if we need to go back to keyguard
// TODO: It's preferable to stay and show the correct lockscreen or unlock if none
- maybeCreateKeyguardLocked(shouldEnableScreenRotation());
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), userSwitched);
}
public synchronized void onScreenTurnedOff() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 59aed24..83324bc 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -291,7 +291,7 @@
public void onUserSwitched(int userId) {
// Note that the mLockPatternUtils user has already been updated from setCurrentUser.
synchronized (KeyguardViewMediator.this) {
- resetStateLocked();
+ resetStateLocked(true);
}
// We should always go back to the locked state when a user
// switch happens. Is there a more direct way to do this?
@@ -351,7 +351,7 @@
+ "device isn't provisioned yet.");
doKeyguardLocked();
} else {
- resetStateLocked();
+ resetStateLocked(false);
}
}
}
@@ -364,7 +364,7 @@
+ "showing; need to show keyguard so user can enter sim pin");
doKeyguardLocked();
} else {
- resetStateLocked();
+ resetStateLocked(false);
}
}
break;
@@ -377,14 +377,14 @@
} else {
if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ "show permanently disabled message in lockscreen.");
- resetStateLocked();
+ resetStateLocked(false);
}
}
break;
case READY:
synchronized (this) {
if (isShowing()) {
- resetStateLocked();
+ resetStateLocked(false);
}
}
break;
@@ -530,7 +530,7 @@
}
} else if (mShowing) {
notifyScreenOffLocked();
- resetStateLocked();
+ resetStateLocked(false);
} else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
|| (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
// if the screen turned off because of timeout or the user hit the power button
@@ -644,7 +644,7 @@
if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
mExitSecureCallback.onKeyguardExitResult(false);
mExitSecureCallback = null;
- resetStateLocked();
+ resetStateLocked(false);
} else {
showLocked();
@@ -805,11 +805,12 @@
/**
* Send message to keyguard telling it to reset its state.
+ * @param userSwitched true if we're resetting state because user switched
* @see #handleReset()
*/
- private void resetStateLocked() {
+ private void resetStateLocked(boolean userSwitched) {
if (DEBUG) Log.d(TAG, "resetStateLocked");
- Message msg = mHandler.obtainMessage(RESET);
+ Message msg = mHandler.obtainMessage(RESET, userSwitched ? 1 : 0, 0);
mHandler.sendMessage(msg);
}
@@ -1046,7 +1047,7 @@
handleHide();
return ;
case RESET:
- handleReset();
+ handleReset(msg.arg1 != 0);
return ;
case VERIFY_UNLOCK:
handleVerifyUnlock();
@@ -1289,13 +1290,13 @@
}
/**
- * Handle message sent by {@link #resetStateLocked()}
+ * Handle message sent by {@link #resetStateLocked(boolean)}
* @see #RESET
*/
- private void handleReset() {
+ private void handleReset(boolean userSwitched) {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleReset");
- mKeyguardViewManager.reset();
+ mKeyguardViewManager.reset(userSwitched);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index d17c128..9d9b043 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -19,13 +19,17 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.Shader;
+import android.os.PowerManager;
+import android.os.SystemClock;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.widget.FrameLayout;
import com.android.internal.R;
@@ -33,13 +37,19 @@
public class KeyguardWidgetFrame extends FrameLayout {
private final static PorterDuffXfermode sAddBlendMode =
new PorterDuffXfermode(PorterDuff.Mode.ADD);
- private static Drawable sLeftOverscrollDrawable;
- private static Drawable sRightOverscrollDrawable;
- private Drawable mForegroundDrawable;
+ private int mGradientColor;
+ private LinearGradient mForegroundGradient;
+ private LinearGradient mLeftToRightGradient;
+ private LinearGradient mRightToLeftGradient;
+ private Paint mGradientPaint = new Paint();
+ boolean mLeftToRight = true;
+
private float mOverScrollAmount = 0f;
private final Rect mForegroundRect = new Rect();
private int mForegroundAlpha = 0;
+ private PowerManager mPowerManager;
+ private boolean mDisableInteraction;
public KeyguardWidgetFrame(Context context) {
this(context, null, 0);
@@ -51,28 +61,42 @@
public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- Resources res = context.getResources();
- if (sLeftOverscrollDrawable == null) {
- sLeftOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_left);
- sRightOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_right);
- }
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+
+ Resources res = context.getResources();
int hPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_horizontal_padding);
int topPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_top_padding);
int bottomPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
setPadding(hPadding, topPadding, hPadding, bottomPadding);
+ mGradientColor = res.getColor(R.color.kg_widget_pager_gradient);
+ mGradientPaint.setXfermode(sAddBlendMode);
+ }
+
+ public void setDisableUserInteraction(boolean disabled) {
+ mDisableInteraction = disabled;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!mDisableInteraction) {
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+ return super.onInterceptTouchEvent(ev);
+ }
+ return true;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- if (mForegroundAlpha > 0) {
- mForegroundDrawable.setBounds(mForegroundRect);
- Paint p = ((NinePatchDrawable) mForegroundDrawable).getPaint();
- p.setXfermode(sAddBlendMode);
- mForegroundDrawable.draw(canvas);
- p.setXfermode(null);
- }
+ drawGradientOverlay(canvas);
+
+ }
+
+ private void drawGradientOverlay(Canvas c) {
+ mGradientPaint.setShader(mForegroundGradient);
+ mGradientPaint.setAlpha(mForegroundAlpha);
+ c.drawRect(mForegroundRect, mGradientPaint);
}
@Override
@@ -80,27 +104,20 @@
super.onSizeChanged(w, h, oldw, oldh);
mForegroundRect.set(getPaddingLeft(), getPaddingTop(),
w - getPaddingRight(), h - getPaddingBottom());
+ float x0 = mLeftToRight ? 0 : mForegroundRect.width();
+ float x1 = mLeftToRight ? mForegroundRect.width(): 0;
+ mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f,
+ mGradientColor, 0, Shader.TileMode.CLAMP);
+ mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
+ mGradientColor, 0, Shader.TileMode.CLAMP);
}
void setOverScrollAmount(float r, boolean left) {
if (Float.compare(mOverScrollAmount, r) != 0) {
mOverScrollAmount = r;
- if (left && mForegroundDrawable != sLeftOverscrollDrawable) {
- mForegroundDrawable = sLeftOverscrollDrawable;
- } else if (!left && mForegroundDrawable != sRightOverscrollDrawable) {
- mForegroundDrawable = sRightOverscrollDrawable;
- }
-
- mForegroundAlpha = (int) Math.round((r * 255));
- mForegroundDrawable.setAlpha(mForegroundAlpha);
- if (getLayerType() != LAYER_TYPE_HARDWARE) {
- setLayerType(LAYER_TYPE_HARDWARE, null);
- }
+ mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient;
+ mForegroundAlpha = (int) Math.round((0.85f * r * 255));
invalidate();
- } else {
- if (getLayerType() != LAYER_TYPE_NONE) {
- setLayerType(LAYER_TYPE_NONE, null);
- }
}
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 4af7a25..9dfbba8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -27,6 +27,8 @@
import android.widget.FrameLayout;
+import com.android.internal.R;
+
public class KeyguardWidgetPager extends PagedView {
ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
private static float CAMERA_DISTANCE = 10000;
@@ -47,6 +49,9 @@
public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
/*
@@ -60,6 +65,7 @@
// The framework adds a default padding to AppWidgetHostView. We don't need this padding
// for the Keyguard, so we override it to be 0.
widget.setPadding(0, 0, 0, 0);
+ widget.setContentDescription(widget.getAppWidgetInfo().label);
frame.addView(widget, lp);
addView(frame);
}
@@ -70,6 +76,25 @@
}
}
+ @Override
+ protected void onPageBeginMoving() {
+ // Enable hardware layers while pages are moving
+ // TODO: We should only do this for the two views that are actually moving
+ int children = getChildCount();
+ for (int i = 0; i < children; i++) {
+ getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+ }
+
+ @Override
+ protected void onPageEndMoving() {
+ // Disable hardware layers while pages are moving
+ int children = getChildCount();
+ for (int i = 0; i < children; i++) {
+ getChildAt(i).setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
/*
* This interpolator emulates the rate at which the perceived scale of an object changes
* as its distance from a camera increases. When this interpolator is applied to a scale
@@ -90,6 +115,21 @@
}
@Override
+ public String getCurrentPageDescription() {
+ final int nextPageIndex = getNextPage();
+ if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) {
+ KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex);
+ CharSequence title = frame.getChildAt(0).getContentDescription();
+ if (title == null) {
+ title = "";
+ }
+ return mContext.getString(R.string.keyguard_accessibility_widget_changed,
+ title, nextPageIndex + 1, getChildCount());
+ }
+ return super.getCurrentPageDescription();
+ }
+
+ @Override
protected void overScroll(float amount) {
acceleratedOverScroll(amount);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
index f7f23c7..e9ea2c3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
@@ -16,6 +16,8 @@
package com.android.internal.policy.impl.keyguard;
import android.content.Context;
+import android.os.PowerManager;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -29,6 +31,7 @@
KeyguardGlowStripView mRightStrip;
KeyguardWidgetPager mPager;
private int mPage = 0;
+ private PowerManager mPowerManager;
public KeyguardWidgetRegion(Context context) {
this(context, null, 0);
@@ -40,6 +43,7 @@
public KeyguardWidgetRegion(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@Override
@@ -70,21 +74,25 @@
@Override
public void onPageSwitch(View newPage, int newPageIndex) {
- mPage = newPageIndex;
-
- // If we're showing the default system status widget, then we want to hide the clock
- boolean hideClock = false;
+ boolean showingStatusWidget = false;
if ((newPage instanceof ViewGroup)) {
ViewGroup vg = (ViewGroup) newPage;
if (vg.getChildAt(0) instanceof KeyguardStatusView) {
- hideClock = true;
+ showingStatusWidget = true;
}
}
- if (hideClock) {
+ // Disable the status bar clock if we're showing the default status widget
+ if (showingStatusWidget) {
setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
} else {
setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
}
+
+ // Extend the display timeout if the user switches pages
+ if (mPage != newPageIndex) {
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+ mPage = newPageIndex;
+ }
}
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 0b4871d..0045f4a 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -40,11 +40,9 @@
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
@@ -69,12 +67,12 @@
* a degree Centigrade</p>
* <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p>
*/
-public class BatteryService extends Binder {
+public final class BatteryService extends Binder {
private static final String TAG = BatteryService.class.getSimpleName();
- private static final boolean LOCAL_LOGV = false;
+ private static final boolean DEBUG = false;
- static final int BATTERY_SCALE = 100; // battery capacity is a percentage
+ private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
@@ -92,6 +90,9 @@
private final Context mContext;
private final IBatteryStats mBatteryStats;
+ private final Object mLock = new Object();
+
+ /* Begin native fields: All of these fields are set by native code. */
private boolean mAcOnline;
private boolean mUsbOnline;
private boolean mWirelessOnline;
@@ -103,7 +104,7 @@
private int mBatteryTemperature;
private String mBatteryTechnology;
private boolean mBatteryLevelCritical;
- private int mInvalidCharger;
+ /* End native fields. */
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -112,6 +113,8 @@
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
+
+ private int mInvalidCharger;
private int mLastInvalidCharger;
private int mLowBatteryWarningLevel;
@@ -128,6 +131,8 @@
private boolean mSentLowBatteryBroadcast = false;
+ private native void native_update();
+
public BatteryService(Context context, LightsService lights) {
mContext = context;
mLed = new Led(context, lights);
@@ -146,83 +151,83 @@
// watch for invalid charger messages if the invalid_charger switch exists
if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
- mInvalidChargerObserver.startObserving("DEVPATH=/devices/virtual/switch/invalid_charger");
+ mInvalidChargerObserver.startObserving(
+ "DEVPATH=/devices/virtual/switch/invalid_charger");
}
// set initial status
- update();
+ synchronized (mLock) {
+ updateLocked();
+ }
}
- public final boolean isPowered() {
- // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work.
- return (mAcOnline || mUsbOnline || mWirelessOnline
- || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
+ void systemReady() {
+ // check our power situation now that it is safe to display the shutdown dialog.
+ synchronized (mLock) {
+ shutdownIfNoPowerLocked();
+ shutdownIfOverTempLocked();
+ }
}
- public final boolean isPowered(int plugTypeSet) {
+ /**
+ * Returns true if the device is plugged into any of the specified plug types.
+ */
+ public boolean isPowered(int plugTypeSet) {
+ synchronized (mLock) {
+ return isPoweredLocked(plugTypeSet);
+ }
+ }
+
+ private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
- if (plugTypeSet == 0) {
- return false;
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mAcOnline) {
+ return true;
}
- int plugTypeBit = 0;
- if (mAcOnline) {
- plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC;
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mUsbOnline) {
+ return true;
}
- if (mUsbOnline) {
- plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB;
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mWirelessOnline) {
+ return true;
}
- if (mWirelessOnline) {
- plugTypeBit |= BatteryManager.BATTERY_PLUGGED_WIRELESS;
- }
- return (plugTypeSet & plugTypeBit) != 0;
+ return false;
}
- public final int getPlugType() {
- return mPlugType;
- }
-
- private UEventObserver mPowerSupplyObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- update();
+ /**
+ * Returns the current plug type.
+ */
+ public int getPlugType() {
+ synchronized (mLock) {
+ return mPlugType;
}
- };
+ }
- private UEventObserver mInvalidChargerObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
- if (mInvalidCharger != invalidCharger) {
- mInvalidCharger = invalidCharger;
- update();
- }
+ /**
+ * Returns battery level as a percentage.
+ */
+ public int getBatteryLevel() {
+ synchronized (mLock) {
+ return mBatteryLevel;
}
- };
-
- // returns battery level as a percentage
- public final int getBatteryLevel() {
- return mBatteryLevel;
}
- // true if battery level is below the first warning threshold
- public final boolean isBatteryLow() {
- return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel;
+ /**
+ * Returns true if battery level is below the first warning threshold.
+ */
+ public boolean isBatteryLow() {
+ synchronized (mLock) {
+ return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel;
+ }
}
- void systemReady() {
- // check our power situation now that it is safe to display the shutdown dialog.
- shutdownIfNoPower();
- shutdownIfOverTemp();
- }
-
- private final void shutdownIfNoPower() {
+ private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
+ if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
+ && ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -230,7 +235,7 @@
}
}
- private final void shutdownIfOverTemp() {
+ private void shutdownIfOverTempLocked() {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
@@ -243,18 +248,19 @@
}
}
- private native void native_update();
-
- private synchronized final void update() {
+ private void updateLocked() {
+ // Update the values of mAcOnline, et. all.
native_update();
- processValues();
+
+ // Process the new values.
+ processValuesLocked();
}
- private void processValues() {
+ private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;
- mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel;
+ mBatteryLevelCritical = (mBatteryLevel <= mCriticalBatteryLevel);
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mUsbOnline) {
@@ -265,6 +271,22 @@
mPlugType = BATTERY_PLUGGED_NONE;
}
+ if (DEBUG) {
+ Slog.d(TAG, "Processing new values: "
+ + "mAcOnline=" + mAcOnline
+ + ", mUsbOnline=" + mUsbOnline
+ + ", mWirelessOnline=" + mWirelessOnline
+ + ", mBatteryStatus=" + mBatteryStatus
+ + ", mBatteryHealth=" + mBatteryHealth
+ + ", mBatteryPresent=" + mBatteryPresent
+ + ", mBatteryLevel=" + mBatteryLevel
+ + ", mBatteryTechnology=" + mBatteryTechnology
+ + ", mBatteryVoltage=" + mBatteryVoltage
+ + ", mBatteryTemperature=" + mBatteryTemperature
+ + ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ + ", mPlugType=" + mPlugType);
+ }
+
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -274,8 +296,8 @@
// Should never happen.
}
- shutdownIfNoPower();
- shutdownIfOverTemp();
+ shutdownIfNoPowerLocked();
+ shutdownIfOverTempLocked();
if (mBatteryStatus != mLastBatteryStatus ||
mBatteryHealth != mLastBatteryHealth ||
@@ -342,7 +364,7 @@
&& mBatteryLevel <= mLowBatteryWarningLevel
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
- sendIntent();
+ sendIntentLocked();
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
@@ -373,7 +395,7 @@
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
- logOutlier(dischargeDuration);
+ logOutlierLocked(dischargeDuration);
}
mLastBatteryStatus = mBatteryStatus;
@@ -388,13 +410,13 @@
}
}
- private final void sendIntent() {
+ private void sendIntentLocked() {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- int icon = getIcon(mBatteryLevel);
+ int icon = getIconLocked(mBatteryLevel);
intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
@@ -408,22 +430,22 @@
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- if (false) {
- Slog.d(TAG, "level:" + mBatteryLevel +
- " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
- " health:" + mBatteryHealth + " present:" + mBatteryPresent +
- " voltage: " + mBatteryVoltage +
- " temperature: " + mBatteryTemperature +
- " technology: " + mBatteryTechnology +
- " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
- " Wireless powered:" + mWirelessOnline +
- " icon:" + icon + " invalid charger:" + mInvalidCharger);
+ if (DEBUG) {
+ Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryLevel +
+ ", scale:" + BATTERY_SCALE + ", status:" + mBatteryStatus +
+ ", health:" + mBatteryHealth + ", present:" + mBatteryPresent +
+ ", voltage: " + mBatteryVoltage +
+ ", temperature: " + mBatteryTemperature +
+ ", technology: " + mBatteryTechnology +
+ ", AC powered:" + mAcOnline + ", USB powered:" + mUsbOnline +
+ ", Wireless powered:" + mWirelessOnline +
+ ", icon:" + icon + ", invalid charger:" + mInvalidCharger);
}
ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
}
- private final void logBatteryStats() {
+ private void logBatteryStatsLocked() {
IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
if (batteryInfoService == null) return;
@@ -461,7 +483,7 @@
}
}
- private final void logOutlier(long duration) {
+ private void logOutlierLocked(long duration) {
ContentResolver cr = mContext.getContentResolver();
String dischargeThresholdString = Settings.Global.getString(cr,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
@@ -475,11 +497,11 @@
if (duration <= durationThreshold &&
mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
// If the discharge cycle is bad enough we want to know about it.
- logBatteryStats();
+ logBatteryStatsLocked();
}
- if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold +
+ if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
" discharge threshold: " + dischargeThreshold);
- if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " +
+ if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
(mDischargeStartLevel - mBatteryLevel));
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
@@ -489,14 +511,15 @@
}
}
- private final int getIcon(int level) {
+ private int getIconLocked(int level) {
if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
return com.android.internal.R.drawable.stat_sys_battery;
} else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
|| mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
- if (isPowered() && mBatteryLevel >= 100) {
+ if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
+ && mBatteryLevel >= 100) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else {
return com.android.internal.R.drawable.stat_sys_battery;
@@ -517,8 +540,8 @@
return;
}
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- synchronized (this) {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
pw.println(" AC powered: " + mAcOnline);
pw.println(" USB powered: " + mUsbOnline);
@@ -531,73 +554,89 @@
pw.println(" voltage:" + mBatteryVoltage);
pw.println(" temperature: " + mBatteryTemperature);
pw.println(" technology: " + mBatteryTechnology);
- }
- } else if (false) {
- // DO NOT SUBMIT WITH THIS TURNED ON
- if (args.length == 3 && "set".equals(args[0])) {
- String key = args[1];
- String value = args[2];
- try {
- boolean update = true;
- if ("ac".equals(key)) {
- mAcOnline = Integer.parseInt(value) != 0;
- } else if ("usb".equals(key)) {
- mUsbOnline = Integer.parseInt(value) != 0;
- } else if ("wireless".equals(key)) {
- mWirelessOnline = Integer.parseInt(value) != 0;
- } else if ("status".equals(key)) {
- mBatteryStatus = Integer.parseInt(value);
- } else if ("level".equals(key)) {
- mBatteryLevel = Integer.parseInt(value);
- } else if ("invalid".equals(key)) {
- mInvalidCharger = Integer.parseInt(value);
- } else {
- update = false;
+ } else if (false) {
+ // DO NOT SUBMIT WITH THIS TURNED ON
+ if (args.length == 3 && "set".equals(args[0])) {
+ String key = args[1];
+ String value = args[2];
+ try {
+ boolean update = true;
+ if ("ac".equals(key)) {
+ mAcOnline = Integer.parseInt(value) != 0;
+ } else if ("usb".equals(key)) {
+ mUsbOnline = Integer.parseInt(value) != 0;
+ } else if ("wireless".equals(key)) {
+ mWirelessOnline = Integer.parseInt(value) != 0;
+ } else if ("status".equals(key)) {
+ mBatteryStatus = Integer.parseInt(value);
+ } else if ("level".equals(key)) {
+ mBatteryLevel = Integer.parseInt(value);
+ } else if ("invalid".equals(key)) {
+ mInvalidCharger = Integer.parseInt(value);
+ } else {
+ update = false;
+ }
+ if (update) {
+ processValuesLocked();
+ }
+ } catch (NumberFormatException ex) {
+ pw.println("Bad value: " + value);
}
- if (update) {
- processValues();
- }
- } catch (NumberFormatException ex) {
- pw.println("Bad value: " + value);
}
}
}
}
- class Led {
- private LightsService mLightsService;
- private LightsService.Light mBatteryLight;
+ private final UEventObserver mPowerSupplyObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ synchronized (mLock) {
+ updateLocked();
+ }
+ }
+ };
- private int mBatteryLowARGB;
- private int mBatteryMediumARGB;
- private int mBatteryFullARGB;
- private int mBatteryLedOn;
- private int mBatteryLedOff;
+ private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+ synchronized (mLock) {
+ if (mInvalidCharger != invalidCharger) {
+ mInvalidCharger = invalidCharger;
+ updateLocked();
+ }
+ }
+ }
+ };
- private boolean mBatteryCharging;
- private boolean mBatteryLow;
- private boolean mBatteryFull;
+ private final class Led {
+ private final LightsService.Light mBatteryLight;
- Led(Context context, LightsService lights) {
- mLightsService = lights;
+ private final int mBatteryLowARGB;
+ private final int mBatteryMediumARGB;
+ private final int mBatteryFullARGB;
+ private final int mBatteryLedOn;
+ private final int mBatteryLedOff;
+
+ public Led(Context context, LightsService lights) {
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
- mBatteryLowARGB = mContext.getResources().getInteger(
+ mBatteryLowARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLowARGB);
- mBatteryMediumARGB = mContext.getResources().getInteger(
+ mBatteryMediumARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
- mBatteryFullARGB = mContext.getResources().getInteger(
+ mBatteryFullARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryFullARGB);
- mBatteryLedOn = mContext.getResources().getInteger(
+ mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
- mBatteryLedOff = mContext.getResources().getInteger(
+ mBatteryLedOff = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOff);
}
/**
* Synchronize on BatteryService.
*/
- void updateLightsLocked() {
+ public void updateLightsLocked() {
final int level = mBatteryLevel;
final int status = mBatteryStatus;
if (level < mLowBatteryWarningLevel) {
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index a4c376d..94a087a 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -405,7 +405,7 @@
notification.setLatestEventInfo(mContext, title, details, intent);
mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
UserHandle.ALL);
- mContext.sendStickyBroadcast(mStorageLowIntent);
+ mContext.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
}
/**
@@ -428,7 +428,7 @@
*/
private final void sendFullNotification() {
if(localLOGV) Slog.i(TAG, "Sending memory full notification");
- mContext.sendStickyBroadcast(mStorageFullIntent);
+ mContext.sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
}
/**
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 2197e31..ae95c4c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -645,12 +645,11 @@
/**
* Returns all providers by name, including passive, but excluding
- * fused.
+ * fused, also including ones that are not permitted to
+ * be accessed by the calling activity or are currently disabled.
*/
@Override
public List<String> getAllProviders() {
- checkPermission();
-
ArrayList<String> out;
synchronized (mLock) {
out = new ArrayList<String>(mProviders.size());
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 9dbe503..1342250 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -329,7 +330,7 @@
* text of why it is not a good time.
*/
String shouldWeBeBrutalLocked(long curTime) {
- if (mBattery == null || !mBattery.isPowered()) {
+ if (mBattery == null || !mBattery.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) {
return "battery";
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 616bc13..c9f89b1 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -198,7 +198,7 @@
private GestureLibrary mGestureLibrary;
// The long pressing pointer id if coordinate remapping is needed.
- private int mLongPressingPointerId;
+ private int mLongPressingPointerId = -1;
// The long pressing pointer X if coordinate remapping is needed.
private int mLongPressingPointerDeltaX;
@@ -702,10 +702,24 @@
}
}
} break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ if (pointerId == mDraggingPointerId) {
+ mDraggingPointerId = INVALID_POINTER_ID;
+ // Send an event to the end of the drag gesture.
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ }
+ } break;
case MotionEvent.ACTION_UP: {
// Announce the end of a new touch interaction.
sendAccessibilityEvent(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ if (pointerId == mDraggingPointerId) {
+ mDraggingPointerId = INVALID_POINTER_ID;
+ // Send an event to the end of the drag gesture.
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ }
mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_CANCEL: {
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index f0cd0f5..b4dab86 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -38,9 +38,15 @@
public static final int FLAG_SUPPORTS_ROTATION = 1 << 1;
/**
- * Flag: Indicates that this display device can show secure surfaces.
+ * Flag: Indicates that this display device has secure video output, such as HDCP.
*/
- public static final int FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT = 1 << 2;
+ public static final int FLAG_SECURE = 1 << 2;
+
+ /**
+ * Flag: Indicates that this display device supports compositing
+ * from gralloc protected buffers.
+ */
+ public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 3;
/**
* Touch attachment: Display does not receive touch.
@@ -182,8 +188,11 @@
if ((flags & FLAG_SUPPORTS_ROTATION) != 0) {
msg.append(", FLAG_SUPPORTS_ROTATION");
}
- if ((flags & FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT) != 0) {
- msg.append(", FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT");
+ if ((flags & FLAG_SECURE) != 0) {
+ msg.append(", FLAG_SECURE");
+ }
+ if ((flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
+ msg.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
return msg.toString();
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index f3bec1d..7ec537f 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -60,7 +60,8 @@
mInfo.xDpi = 160;
mInfo.yDpi = 160;
mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ | DisplayDeviceInfo.FLAG_SECURE
+ | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 9c51463..679a67e 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -124,11 +124,16 @@
mInfo.width = mPhys.width;
mInfo.height = mPhys.height;
mInfo.refreshRate = mPhys.refreshRate;
+
+ // Assume that all built-in displays have secure output (eg. HDCP) and
+ // support compositing from gralloc protected buffers.
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
+ | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+
if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_built_in_display_name);
- mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
mInfo.xDpi = mPhys.xDpi;
@@ -137,7 +142,6 @@
} else {
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
- mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
}
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 3607de15..c4b749c 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -179,8 +179,8 @@
if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {
mBaseDisplayInfo.layerStack = mLayerStack;
mBaseDisplayInfo.flags = 0;
- if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT) != 0) {
- mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.appWidth = deviceInfo.width;
@@ -299,7 +299,9 @@
}
public void dumpLocked(PrintWriter pw) {
+ pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mHasContent=" + mHasContent);
pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
mPrimaryDisplayDevice.getNameLocked() : "null"));
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index 0767fc0..dfacf2a 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -19,14 +19,11 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.IBinder;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -192,28 +189,42 @@
private final int mDensityDpi;
private Surface mSurface;
+ private SurfaceTexture mSurfaceTexture;
private DisplayDeviceInfo mInfo;
public OverlayDisplayDevice(IBinder displayToken, String name,
int width, int height, float refreshRate, int densityDpi,
- Surface surface) {
+ SurfaceTexture surfaceTexture) {
super(OverlayDisplayAdapter.this, displayToken);
mName = name;
mWidth = width;
mHeight = height;
mRefreshRate = refreshRate;
mDensityDpi = densityDpi;
- mSurface = surface;
+ mSurfaceTexture = surfaceTexture;
}
- public void clearSurfaceLocked() {
- mSurface = null;
+ public void clearSurfaceTextureLocked() {
+ if (mSurfaceTexture != null) {
+ mSurfaceTexture = null;
+ }
sendTraversalRequestLocked();
}
@Override
public void performTraversalInTransactionLocked() {
- setSurfaceInTransactionLocked(mSurface);
+ if (mSurfaceTexture != null) {
+ if (mSurface == null) {
+ mSurface = new Surface(mSurfaceTexture);
+ }
+ setSurfaceInTransactionLocked(mSurface);
+ } else {
+ setSurfaceInTransactionLocked(null);
+ if (mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ }
}
@Override
@@ -227,7 +238,7 @@
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
- mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ mInfo.flags = 0;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
@@ -268,11 +279,11 @@
// Called on the UI thread.
@Override
- public void onWindowCreated(Surface surface, float refreshRate) {
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
synchronized (getSyncRoot()) {
IBinder displayToken = Surface.createDisplay(mName);
mDevice = new OverlayDisplayDevice(displayToken, mName,
- mWidth, mHeight, refreshRate, mDensityDpi, surface);
+ mWidth, mHeight, refreshRate, mDensityDpi, surfaceTexture);
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
}
@@ -283,7 +294,7 @@
public void onWindowDestroyed() {
synchronized (getSyncRoot()) {
if (mDevice != null) {
- mDevice.clearSurfaceLocked();
+ mDevice.clearSurfaceTextureLocked();
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
diff --git a/services/java/com/android/server/display/OverlayDisplayWindow.java b/services/java/com/android/server/display/OverlayDisplayWindow.java
index d08f65f..a0edced 100644
--- a/services/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/java/com/android/server/display/OverlayDisplayWindow.java
@@ -29,7 +29,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
-import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
@@ -146,6 +145,7 @@
}
}
+ @Override
public void dump(PrintWriter pw) {
pw.println("mWindowVisible=" + mWindowVisible);
pw.println("mWindowX=" + mWindowX);
@@ -291,8 +291,7 @@
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
- mListener.onWindowCreated(new Surface(surfaceTexture),
- mDefaultDisplayInfo.refreshRate);
+ mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate);
}
@Override
@@ -361,7 +360,7 @@
* Watches for significant changes in the overlay display window lifecycle.
*/
public interface Listener {
- public void onWindowCreated(Surface surface, float refreshRate);
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate);
public void onWindowDestroyed();
}
}
\ No newline at end of file
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 4a89be7..b2beb5e 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -50,7 +50,8 @@
final class WifiDisplayAdapter extends DisplayAdapter {
private static final String TAG = "WifiDisplayAdapter";
- private PersistentDataStore mPersistentDataStore;
+ private final PersistentDataStore mPersistentDataStore;
+ private final boolean mSupportsProtectedBuffers;
private WifiDisplayController mDisplayController;
private WifiDisplayDevice mDisplayDevice;
@@ -70,6 +71,8 @@
PersistentDataStore persistentDataStore) {
super(syncRoot, context, handler, listener, TAG);
mPersistentDataStore = persistentDataStore;
+ mSupportsProtectedBuffers = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
}
@Override
@@ -84,6 +87,7 @@
pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
+ pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
// Try to dump the controller state.
if (mDisplayController == null) {
@@ -217,7 +221,10 @@
int deviceFlags = 0;
if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
- deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
+ }
+ if (mSupportsProtectedBuffers) {
+ deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
float refreshRate = 60.0f; // TODO: get this for real
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 564a805..0f3dc92 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -851,7 +851,8 @@
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, args.packageURI,
- PackageManager.VERIFICATION_ALLOW);
+ PackageManager.VERIFICATION_ALLOW,
+ state.getInstallArgs().getUser());
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
@@ -859,7 +860,8 @@
}
} else {
broadcastPackageVerified(verificationId, args.packageURI,
- PackageManager.VERIFICATION_REJECT);
+ PackageManager.VERIFICATION_REJECT,
+ state.getInstallArgs().getUser());
}
processPendingInstall(args, ret);
@@ -889,7 +891,7 @@
if (state.isInstallAllowed()) {
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
broadcastPackageVerified(verificationId, args.packageURI,
- response.code);
+ response.code, state.getInstallArgs().getUser());
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
@@ -5741,14 +5743,15 @@
}
private void broadcastPackageVerified(int verificationId, Uri packageUri,
- int verificationCode) {
+ int verificationCode, UserHandle user) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
- mContext.sendBroadcast(intent, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
+ mContext.sendBroadcastAsUser(intent, user,
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}
private ComponentName matchComponentForVerifier(String packageName,
@@ -6398,7 +6401,7 @@
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
- : getPackageUid(mRequiredVerifierPackage, 0);
+ : getPackageUid(mRequiredVerifierPackage, getUser().getIdentifier());
if (requiredUid != -1 && isVerificationEnabled(flags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
@@ -6477,7 +6480,7 @@
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
- mContext.sendBroadcast(sufficientIntent);
+ mContext.sendBroadcastAsUser(sufficientIntent, getUser());
}
}
}
@@ -6492,7 +6495,7 @@
* target BroadcastReceivers have run.
*/
verification.setComponent(requiredVerifierComponent);
- mContext.sendOrderedBroadcast(verification,
+ mContext.sendOrderedBroadcastAsUser(verification, getUser(),
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
new BroadcastReceiver() {
@Override
@@ -6779,6 +6782,10 @@
protected boolean isFwdLocked() {
return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
}
+
+ UserHandle getUser() {
+ return user;
+ }
}
class FileInstallArgs extends InstallArgs {
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 6a363a8..ae1b213 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -65,8 +65,7 @@
boolean permissionsFixed;
boolean haveGids;
- private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState(false);
- private static final PackageUserState DEFAULT_SYSTEM_USER_STATE = new PackageUserState(true);
+ private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
@@ -176,7 +175,7 @@
private PackageUserState modifyUserState(int userId) {
PackageUserState state = userState.get(userId);
if (state == null) {
- state = new PackageUserState((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0);
+ state = new PackageUserState();
userState.put(userId, state);
}
return state;
@@ -187,8 +186,7 @@
if (state != null) {
return state;
}
- return ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? DEFAULT_SYSTEM_USER_STATE : DEFAULT_USER_STATE;
+ return DEFAULT_USER_STATE;
}
void setEnabled(int state, int userId) {
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 664125a..c91fa3c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -130,11 +130,22 @@
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000;
- // The screen dim duration, in seconds.
+ // The screen dim duration, in milliseconds.
// This is subtracted from the end of the screen off timeout so the
// minimum screen off timeout should be longer than this.
private static final int SCREEN_DIM_DURATION = 7 * 1000;
+ // The maximum screen dim time expressed as a ratio relative to the screen
+ // off timeout. If the screen off timeout is very short then we want the
+ // dim timeout to also be quite short so that most of the time is spent on.
+ // Otherwise the user won't get much screen on time before dimming occurs.
+ private static final float MAXIMUM_SCREEN_DIM_RATIO = 0.2f;
+
+ // Upper bound on the battery charge percentage in order to consider turning
+ // the screen on when the device starts charging wirelessly.
+ // See point of use for more details.
+ private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
+
private Context mContext;
private LightsService mLightsService;
private BatteryService mBatteryService;
@@ -218,6 +229,9 @@
// True if the device is plugged into a power source.
private boolean mIsPowered;
+ // The current plug type, such as BatteryManager.BATTERY_PLUGGED_WIRELESS.
+ private int mPlugType;
+
// True if the device should wake up when plugged or unplugged.
private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
@@ -1013,10 +1027,19 @@
*/
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
- boolean wasPowered = mIsPowered;
- mIsPowered = mBatteryService.isPowered();
+ final boolean wasPowered = mIsPowered;
+ final int oldPlugType = mPlugType;
+ mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ mPlugType = mBatteryService.getPlugType();
- if (wasPowered != mIsPowered) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
+ + ", mIsPowered=" + mIsPowered
+ + ", oldPlugType=" + oldPlugType
+ + ", mPlugType=" + mPlugType);
+ }
+
+ if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
mDirty |= DIRTY_IS_POWERED;
// Treat plugging and unplugging the devices as a user activity.
@@ -1025,7 +1048,7 @@
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
final long now = SystemClock.uptimeMillis();
- if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+ if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType)) {
wakeUpNoUpdateLocked(now);
}
userActivityNoUpdateLocked(
@@ -1034,6 +1057,44 @@
}
}
+ private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) {
+ if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+ // FIXME: Need more accurate detection of wireless chargers.
+ //
+ // We are unable to accurately detect whether the device is resting on the
+ // charger unless it is actually receiving power. This causes us some grief
+ // because the device might not appear to be plugged into the wireless charger
+ // unless it actually charging.
+ //
+ // To avoid spuriously waking the screen, we apply a special policy to
+ // wireless chargers.
+ //
+ // 1. Don't wake the device when unplugged from wireless charger because
+ // it might be that the device is still resting on the wireless charger
+ // but is not receiving power anymore because the battery is full.
+ //
+ // 2. Don't wake the device when plugged into a wireless charger if the
+ // battery already appears to be mostly full. This situation may indicate
+ // that the device was resting on the charger the whole time and simply
+ // wasn't receiving power because the battery was full. We can't tell
+ // whether the device was just placed on the charger or whether it has
+ // been there for half of the night slowly discharging until it hit
+ // the point where it needed to start charging again.
+ if (wasPowered && !mIsPowered
+ && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+ return false;
+ }
+ if (!wasPowered && mIsPowered
+ && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
+ && mBatteryService.getBatteryLevel() >=
+ WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
/**
* Updates the value of mStayOn.
* Sets DIRTY_STAY_ON if a change occurred.
@@ -1113,7 +1174,7 @@
long nextTimeout = 0;
if (mWakefulness != WAKEFULNESS_ASLEEP) {
final int screenOffTimeout = getScreenOffTimeoutLocked();
- final int screenDimDuration = getScreenDimDurationLocked();
+ final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
@@ -1187,8 +1248,9 @@
return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT);
}
- private int getScreenDimDurationLocked() {
- return SCREEN_DIM_DURATION;
+ private int getScreenDimDurationLocked(int screenOffTimeout) {
+ return Math.min(SCREEN_DIM_DURATION,
+ (int)(screenOffTimeout * MAXIMUM_SCREEN_DIM_RATIO));
}
/**
@@ -1220,15 +1282,25 @@
return changed;
}
- // Also used when exiting a dream to determine whether we should go back
- // to being fully awake or else go to sleep for good.
+ /**
+ * Returns true if the device should go to sleep now.
+ * Also used when exiting a dream to determine whether we should go back
+ * to being fully awake or else go to sleep for good.
+ */
private boolean isItBedTimeYetLocked() {
- return mBootCompleted && !mStayOn
- && (mWakeLockSummary
- & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
- | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) == 0
- && (mUserActivitySummary
- & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0;
+ return mBootCompleted && !isScreenBeingKeptOnLocked();
+ }
+
+ /**
+ * Returns true if the screen is being kept on by a wake lock, user activity
+ * or the stay on while powered setting.
+ */
+ private boolean isScreenBeingKeptOnLocked() {
+ return mStayOn
+ || (mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
+ | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) != 0
+ || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
+ | USER_ACTIVITY_SCREEN_DIM)) != 0;
}
/**
@@ -1236,6 +1308,9 @@
*/
private void updateDreamLocked(int dirty) {
if ((dirty & (DIRTY_WAKEFULNESS
+ | DIRTY_USER_ACTIVITY
+ | DIRTY_WAKE_LOCKS
+ | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS
| DIRTY_IS_POWERED
| DIRTY_STAY_ON
@@ -1318,15 +1393,15 @@
}
/**
- * Returns true if the device is allowed to dream in its current state,
- * assuming that there was either an explicit request to nap or the user activity
- * timeout expired and no wake locks are held.
+ * Returns true if the device is allowed to dream in its current state
+ * assuming that it is currently napping or dreaming.
*/
private boolean canDreamLocked() {
- return mIsPowered
- && mDreamsSupportedConfig
+ return mDreamsSupportedConfig
&& mDreamsEnabledSetting
- && mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
+ && mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
+ && mBootCompleted
+ && (mIsPowered || isScreenBeingKeptOnLocked());
}
/**
@@ -1886,6 +1961,7 @@
pw.println(" mDirty=0x" + Integer.toHexString(mDirty));
pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness));
pw.println(" mIsPowered=" + mIsPowered);
+ pw.println(" mPlugType=" + mPlugType);
pw.println(" mStayOn=" + mStayOn);
pw.println(" mBootCompleted=" + mBootCompleted);
pw.println(" mSystemReady=" + mSystemReady);
@@ -1931,6 +2007,12 @@
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+ final int screenOffTimeout = getScreenOffTimeoutLocked();
+ final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ pw.println();
+ pw.println("Screen off timeout: " + screenOffTimeout + " ms");
+ pw.println("Screen dim duration: " + screenDimDuration + " ms");
+
pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 10011aa..95797ef 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,9 +16,9 @@
package com.android.server.usb;
-import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -30,23 +30,19 @@
import android.database.ContentObserver;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
import android.provider.Settings;
import android.util.Pair;
import android.util.Slog;
@@ -56,10 +52,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
@@ -106,9 +101,12 @@
private UsbHandler mHandler;
private boolean mBootCompleted;
+ private final Object mLock = new Object();
+
private final Context mContext;
private final ContentResolver mContentResolver;
- private final UsbSettingsManager mSettingsManager;
+ // @GuardedBy("mLock")
+ private UsbSettingsManager mCurrentSettings;
private NotificationManager mNotificationManager;
private final boolean mHasUsbAccessory;
private boolean mUseUsbNotification;
@@ -149,10 +147,9 @@
}
};
- public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) {
+ public UsbDeviceManager(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
- mSettingsManager = settingsManager;
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
initRndisAddress();
@@ -175,6 +172,18 @@
}
}
+ public void setCurrentSettings(UsbSettingsManager settings) {
+ synchronized (mLock) {
+ mCurrentSettings = settings;
+ }
+ }
+
+ private UsbSettingsManager getCurrentSettings() {
+ synchronized (mLock) {
+ return mCurrentSettings;
+ }
+ }
+
public void systemReady() {
if (DEBUG) Slog.d(TAG, "systemReady");
@@ -516,7 +525,7 @@
Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
// defer accessoryAttached if system is not ready
if (mBootCompleted) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
+ getCurrentSettings().accessoryAttached(mCurrentAccessory);
} // else handle in mBootCompletedReceiver
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -529,7 +538,7 @@
if (mCurrentAccessory != null) {
if (mBootCompleted) {
- mSettingsManager.accessoryDetached(mCurrentAccessory);
+ getCurrentSettings().accessoryDetached(mCurrentAccessory);
}
mCurrentAccessory = null;
mAccessoryStrings = null;
@@ -618,7 +627,7 @@
case MSG_BOOT_COMPLETED:
mBootCompleted = true;
if (mCurrentAccessory != null) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
+ getCurrentSettings().accessoryAttached(mCurrentAccessory);
}
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(mAdbEnabled);
@@ -774,7 +783,7 @@
+ currentAccessory;
throw new IllegalArgumentException(error);
}
- mSettingsManager.checkPermission(accessory);
+ getCurrentSettings().checkPermission(accessory);
return nativeOpenAccessory();
}
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 0a0ff59..175ae6f 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -16,35 +16,19 @@
package com.android.server.usb;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
-import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
-import android.os.UEventObserver;
-import android.provider.Settings;
+import android.os.Parcelable;
import android.util.Slog;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileReader;
import java.io.PrintWriter;
import java.util.HashMap;
-import java.util.List;
/**
* UsbHostManager manages USB state in host mode.
@@ -54,22 +38,35 @@
private static final boolean LOG = false;
// contains all connected USB devices
- private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
+ private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
// USB busses to exclude from USB host support
private final String[] mHostBlacklist;
private final Context mContext;
private final Object mLock = new Object();
- private final UsbSettingsManager mSettingsManager;
- public UsbHostManager(Context context, UsbSettingsManager settingsManager) {
+ // @GuardedBy("mLock")
+ private UsbSettingsManager mCurrentSettings;
+
+ public UsbHostManager(Context context) {
mContext = context;
- mSettingsManager = settingsManager;
mHostBlacklist = context.getResources().getStringArray(
com.android.internal.R.array.config_usbHostBlacklist);
}
+ public void setCurrentSettings(UsbSettingsManager settings) {
+ synchronized (mLock) {
+ mCurrentSettings = settings;
+ }
+ }
+
+ private UsbSettingsManager getCurrentSettings() {
+ synchronized (mLock) {
+ return mCurrentSettings;
+ }
+ }
+
private boolean isBlackListed(String deviceName) {
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
@@ -154,7 +151,7 @@
UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
deviceClass, deviceSubclass, deviceProtocol, interfaces);
mDevices.put(deviceName, device);
- mSettingsManager.deviceAttached(device);
+ getCurrentSettings().deviceAttached(device);
}
}
@@ -163,7 +160,7 @@
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
- mSettingsManager.deviceDetached(device);
+ getCurrentSettings().deviceDetached(device);
}
}
}
@@ -202,7 +199,7 @@
throw new IllegalArgumentException(
"device " + deviceName + " does not exist or is restricted");
}
- mSettingsManager.checkPermission(device);
+ getCurrentSettings().checkPermission(device);
return nativeOpenDevice(deviceName);
}
}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index bebcd56..629f5fa 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -17,15 +17,20 @@
package com.android.server.usb;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
-import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
import java.io.FileDescriptor;
@@ -37,21 +42,72 @@
* support is delegated to UsbDeviceManager.
*/
public class UsbService extends IUsbManager.Stub {
+ private static final String TAG = "UsbService";
+
private final Context mContext;
+
private UsbDeviceManager mDeviceManager;
private UsbHostManager mHostManager;
- private final UsbSettingsManager mSettingsManager;
+ private final Object mLock = new Object();
+
+ /** Map from {@link UserHandle} to {@link UsbSettingsManager} */
+ // @GuardedBy("mLock")
+ private final SparseArray<UsbSettingsManager>
+ mSettingsByUser = new SparseArray<UsbSettingsManager>();
+
+ private UsbSettingsManager getSettingsForUser(int userId) {
+ synchronized (mLock) {
+ UsbSettingsManager settings = mSettingsByUser.get(userId);
+ if (settings == null) {
+ settings = new UsbSettingsManager(mContext, new UserHandle(userId));
+ mSettingsByUser.put(userId, settings);
+ }
+ return settings;
+ }
+ }
public UsbService(Context context) {
mContext = context;
- mSettingsManager = new UsbSettingsManager(context);
- PackageManager pm = mContext.getPackageManager();
+
+ final PackageManager pm = mContext.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
- mHostManager = new UsbHostManager(context, mSettingsManager);
+ mHostManager = new UsbHostManager(context);
}
if (new File("/sys/class/android_usb").exists()) {
- mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
+ mDeviceManager = new UsbDeviceManager(context);
+ }
+
+ setCurrentUser(UserHandle.USER_OWNER);
+
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ userFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiver(mUserReceiver, userFilter, null, null);
+ }
+
+ private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ setCurrentUser(userId);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ synchronized (mLock) {
+ mSettingsByUser.remove(userId);
+ }
+ }
+ }
+ };
+
+ private void setCurrentUser(int userId) {
+ final UsbSettingsManager userSettings = getSettingsForUser(userId);
+ if (mHostManager != null) {
+ mHostManager.setCurrentSettings(userSettings);
+ }
+ if (mDeviceManager != null) {
+ mDeviceManager.setCurrentSettings(userSettings);
}
}
@@ -65,6 +121,7 @@
}
/* Returns a list of all currently attached USB devices (host mdoe) */
+ @Override
public void getDeviceList(Bundle devices) {
if (mHostManager != null) {
mHostManager.getDeviceList(devices);
@@ -72,6 +129,7 @@
}
/* Opens the specified USB device (host mode) */
+ @Override
public ParcelFileDescriptor openDevice(String deviceName) {
if (mHostManager != null) {
return mHostManager.openDevice(deviceName);
@@ -81,6 +139,7 @@
}
/* returns the currently attached USB accessory (device mode) */
+ @Override
public UsbAccessory getCurrentAccessory() {
if (mDeviceManager != null) {
return mDeviceManager.getCurrentAccessory();
@@ -90,6 +149,7 @@
}
/* opens the currently attached USB accessory (device mode) */
+ @Override
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
return mDeviceManager.openAccessory(accessory);
@@ -98,54 +158,70 @@
}
}
- public void setDevicePackage(UsbDevice device, String packageName) {
+ @Override
+ public void setDevicePackage(UsbDevice device, String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mSettingsManager.setDevicePackage(device, packageName);
+ getSettingsForUser(userId).setDevicePackage(device, packageName);
}
- public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
+ @Override
+ public void setAccessoryPackage(UsbAccessory accessory, String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mSettingsManager.setAccessoryPackage(accessory, packageName);
+ getSettingsForUser(userId).setAccessoryPackage(accessory, packageName);
}
+ @Override
public boolean hasDevicePermission(UsbDevice device) {
- return mSettingsManager.hasPermission(device);
+ final int userId = UserHandle.getCallingUserId();
+ return getSettingsForUser(userId).hasPermission(device);
}
+ @Override
public boolean hasAccessoryPermission(UsbAccessory accessory) {
- return mSettingsManager.hasPermission(accessory);
+ final int userId = UserHandle.getCallingUserId();
+ return getSettingsForUser(userId).hasPermission(accessory);
}
- public void requestDevicePermission(UsbDevice device, String packageName,
- PendingIntent pi) {
- mSettingsManager.requestPermission(device, packageName, pi);
+ @Override
+ public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
+ final int userId = UserHandle.getCallingUserId();
+ getSettingsForUser(userId).requestPermission(device, packageName, pi);
}
- public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
- PendingIntent pi) {
- mSettingsManager.requestPermission(accessory, packageName, pi);
+ @Override
+ public void requestAccessoryPermission(
+ UsbAccessory accessory, String packageName, PendingIntent pi) {
+ final int userId = UserHandle.getCallingUserId();
+ getSettingsForUser(userId).requestPermission(accessory, packageName, pi);
}
+ @Override
public void grantDevicePermission(UsbDevice device, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mSettingsManager.grantDevicePermission(device, uid);
+ final int userId = UserHandle.getUserId(uid);
+ getSettingsForUser(userId).grantDevicePermission(device, uid);
}
+ @Override
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mSettingsManager.grantAccessoryPermission(accessory, uid);
+ final int userId = UserHandle.getUserId(uid);
+ getSettingsForUser(userId).grantAccessoryPermission(accessory, uid);
}
- public boolean hasDefaults(String packageName) {
+ @Override
+ public boolean hasDefaults(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- return mSettingsManager.hasDefaults(packageName);
+ return getSettingsForUser(userId).hasDefaults(packageName);
}
- public void clearDefaults(String packageName) {
+ @Override
+ public void clearDefaults(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mSettingsManager.clearDefaults(packageName);
+ getSettingsForUser(userId).clearDefaults(packageName);
}
+ @Override
public void setCurrentFunction(String function, boolean makeDefault) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
if (mDeviceManager != null) {
@@ -155,6 +231,7 @@
}
}
+ @Override
public void setMassStorageBackingFile(String path) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
if (mDeviceManager != null) {
@@ -164,34 +241,41 @@
}
}
+ @Override
public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
}
+ @Override
public void denyUsbDebugging() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.denyUsbDebugging();
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump UsbManager from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.println("USB Manager State:");
-
if (mDeviceManager != null) {
mDeviceManager.dump(fd, pw);
}
if (mHostManager != null) {
mHostManager.dump(fd, pw);
}
- mSettingsManager.dump(fd, pw);
+
+ synchronized (mLock) {
+ for (int i = 0; i < mSettingsByUser.size(); i++) {
+ final int userId = mSettingsByUser.keyAt(i);
+ final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
+ pw.increaseIndent();
+ pw.println("Settings for user " + userId + ":");
+ settings.dump(fd, pw);
+ pw.decreaseIndent();
+ }
+ }
+ pw.decreaseIndent();
}
}
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index a8453d3..4b2bbfe 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -33,9 +33,10 @@
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Binder;
-import android.os.FileUtils;
-import android.os.Process;
+import android.os.Environment;
import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
@@ -48,7 +49,6 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -60,13 +60,21 @@
import java.util.HashMap;
import java.util.List;
-class UsbSettingsManager {
+import libcore.io.IoUtils;
+class UsbSettingsManager {
private static final String TAG = "UsbSettingsManager";
private static final boolean DEBUG = false;
- private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
+
+ /** Legacy settings file, before multi-user */
+ private static final File sSingleUserSettingsFile = new File(
+ "/data/system/usb_device_manager.xml");
+
+ private final UserHandle mUser;
+ private final AtomicFile mSettingsFile;
private final Context mContext;
+ private final Context mUserContext;
private final PackageManager mPackageManager;
// Temporary mapping USB device name to list of UIDs with permissions for the device
@@ -350,28 +358,49 @@
}
private class MyPackageMonitor extends PackageMonitor {
-
+ @Override
public void onPackageAdded(String packageName, int uid) {
handlePackageUpdate(packageName);
}
+ @Override
public void onPackageChanged(String packageName, int uid, String[] components) {
handlePackageUpdate(packageName);
}
+ @Override
public void onPackageRemoved(String packageName, int uid) {
clearDefaults(packageName);
}
}
+
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
- public UsbSettingsManager(Context context) {
+ public UsbSettingsManager(Context context, UserHandle user) {
+ if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
+
+ try {
+ mUserContext = context.createPackageContextAsUser("android", 0, user);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("Missing android package");
+ }
+
mContext = context;
- mPackageManager = context.getPackageManager();
+ mPackageManager = mUserContext.getPackageManager();
+
+ mUser = user;
+ mSettingsFile = new AtomicFile(new File(
+ Environment.getUserSystemDirectory(user.getIdentifier()),
+ "usb_device_manager.xml"));
+
synchronized (mLock) {
+ if (UserHandle.OWNER.equals(user)) {
+ upgradeSingleUserLocked();
+ }
readSettingsLocked();
}
- mPackageMonitor.register(context, null, true);
+
+ mPackageMonitor.register(mUserContext, null, true);
}
private void readPreference(XmlPullParser parser)
@@ -395,10 +424,54 @@
XmlUtils.nextElement(parser);
}
+ /**
+ * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
+ * Should only by called by owner.
+ */
+ private void upgradeSingleUserLocked() {
+ if (sSingleUserSettingsFile.exists()) {
+ mDevicePreferenceMap.clear();
+ mAccessoryPreferenceMap.clear();
+
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(sSingleUserSettingsFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ final String tagName = parser.getName();
+ if ("preference".equals(tagName)) {
+ readPreference(parser);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed to read single-user settings", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Failed to read single-user settings", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+
+ writeSettingsLocked();
+
+ // Success or failure, we delete single-user file
+ sSingleUserSettingsFile.delete();
+ }
+ }
+
private void readSettingsLocked() {
+ if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
+
+ mDevicePreferenceMap.clear();
+ mAccessoryPreferenceMap.clear();
+
FileInputStream stream = null;
try {
- stream = new FileInputStream(sSettingsFile);
+ stream = mSettingsFile.openRead();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
@@ -407,7 +480,7 @@
String tagName = parser.getName();
if ("preference".equals(tagName)) {
readPreference(parser);
- } else {
+ } else {
XmlUtils.nextElement(parser);
}
}
@@ -415,25 +488,21 @@
if (DEBUG) Slog.d(TAG, "settings file not found");
} catch (Exception e) {
Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
- sSettingsFile.delete();
+ mSettingsFile.delete();
} finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- }
- }
+ IoUtils.closeQuietly(stream);
}
}
private void writeSettingsLocked() {
+ if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
+
FileOutputStream fos = null;
try {
- FileOutputStream fstr = new FileOutputStream(sSettingsFile);
- if (DEBUG) Slog.d(TAG, "writing settings to " + fstr);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
+ fos = mSettingsFile.startWrite();
+
FastXmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(str, "utf-8");
+ serializer.setOutput(fos, "utf-8");
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, "settings");
@@ -455,12 +524,12 @@
serializer.endTag(null, "settings");
serializer.endDocument();
- str.flush();
- FileUtils.sync(fstr);
- str.close();
- } catch (Exception e) {
- Slog.e(TAG, "error writing settings file, deleting to start fresh", e);
- sSettingsFile.delete();
+ mSettingsFile.finishWrite(fos);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write settings", e);
+ if (fos != null) {
+ mSettingsFile.failWrite(fos);
+ }
}
}
@@ -547,7 +616,7 @@
}
// Send broadcast to running activity with registered intent
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mUserContext.sendBroadcast(intent);
// Start activity with registered intent
resolveActivity(intent, matches, defaultPackage, device, null);
@@ -608,7 +677,7 @@
dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
dialogIntent.putExtra("uri", uri);
try {
- mContext.startActivity(dialogIntent);
+ mUserContext.startActivityAsUser(dialogIntent, mUser);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
}
@@ -656,7 +725,7 @@
intent.setComponent(
new ComponentName(defaultRI.activityInfo.packageName,
defaultRI.activityInfo.name));
- mContext.startActivity(intent);
+ mUserContext.startActivityAsUser(intent, mUser);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "startActivity failed", e);
}
@@ -683,7 +752,7 @@
resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
}
try {
- mContext.startActivity(resolverIntent);
+ mUserContext.startActivityAsUser(resolverIntent, mUser);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "unable to start activity " + resolverIntent);
}
@@ -814,7 +883,7 @@
}
private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
- int uid = Binder.getCallingUid();
+ final int uid = Binder.getCallingUid();
// compare uid with packageName to foil apps pretending to be someone else
try {
@@ -833,9 +902,9 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_INTENT, pi);
intent.putExtra("package", packageName);
- intent.putExtra("uid", uid);
+ intent.putExtra(Intent.EXTRA_UID, uid);
try {
- mContext.startActivity(intent);
+ mUserContext.startActivityAsUser(intent, mUser);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "unable to start UsbPermissionActivity");
} finally {
@@ -851,7 +920,7 @@
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
- pi.send(mContext, 0, intent);
+ pi.send(mUserContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
}
@@ -864,14 +933,14 @@
}
public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
- Intent intent = new Intent();
+ Intent intent = new Intent();
// respond immediately if permission has already been granted
if (hasPermission(accessory)) {
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
- try {
- pi.send(mContext, 0, intent);
+ try {
+ pi.send(mUserContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
}
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 9bca834..5874202 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -30,37 +30,37 @@
* all state used for dim animation.
*/
class DimAnimator {
+ static final String TAG = "DimAnimator";
+
Surface mDimSurface;
boolean mDimShown = false;
float mDimCurrentAlpha;
float mDimTargetAlpha;
float mDimDeltaPerMs;
long mLastDimAnimTime;
-
+
int mLastDimWidth, mLastDimHeight;
DimAnimator (SurfaceSession session, final int layerStack) {
- if (mDimSurface == null) {
- try {
- if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
- "DimAnimator",
- 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM | Surface.HIDDEN);
- } else {
- mDimSurface = new Surface(session, "DimAnimator",
- 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM | Surface.HIDDEN);
- }
- if (WindowManagerService.SHOW_TRANSACTIONS ||
- WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
- " DIM " + mDimSurface + ": CREATE");
- mDimSurface.setLayerStack(layerStack);
- mDimSurface.setAlpha(0.0f);
- mDimSurface.show();
- } catch (Exception e) {
- Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
+ try {
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
+ "DimAnimator",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
+ } else {
+ mDimSurface = new Surface(session, "DimAnimator",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
+ if (WindowManagerService.SHOW_TRANSACTIONS ||
+ WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
+ " DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
+ mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
+ } catch (Exception e) {
+ Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
}
@@ -69,6 +69,11 @@
* {@link #updateSurface} after all windows are examined.
*/
void updateParameters(final Resources res, final Parameters params, final long currentTime) {
+ if (mDimSurface == null) {
+ Slog.e(TAG, "updateParameters: no Surface");
+ return;
+ }
+
// Multiply by 1.5 so that rotating a frozen surface that includes this does not expose a
// corner.
final int dw = (int) (params.mDimWidth * 1.5);
@@ -133,6 +138,11 @@
* false when the animation is finished and the dim surface is hidden.
*/
boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) {
+ if (mDimSurface == null) {
+ Slog.e(TAG, "updateSurface: no Surface");
+ return false;
+ }
+
if (!dimming) {
if (mDimTargetAlpha != 0) {
mLastDimAnimTime = currentTime;
@@ -187,6 +197,13 @@
return animating;
}
+ public void kill() {
+ if (mDimSurface != null) {
+ mDimSurface.destroy();
+ mDimSurface = null;
+ }
+ }
+
public void printTo(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("mDimSurface="); pw.print(mDimSurface);
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
index ddbd70d..4225868 100644
--- a/services/java/com/android/server/wm/DimSurface.java
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -24,6 +24,8 @@
import java.io.PrintWriter;
class DimSurface {
+ static final String TAG = "DimSurface";
+
Surface mDimSurface;
boolean mDimShown = false;
int mDimColor = 0;
@@ -31,27 +33,25 @@
int mLastDimWidth, mLastDimHeight;
DimSurface(SurfaceSession session, final int layerStack) {
- if (mDimSurface == null) {
- try {
- if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
- "DimSurface",
- 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM | Surface.HIDDEN);
- } else {
- mDimSurface = new Surface(session, "DimSurface",
- 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM | Surface.HIDDEN);
- }
- if (WindowManagerService.SHOW_TRANSACTIONS ||
- WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
- " DIM " + mDimSurface + ": CREATE");
- mDimSurface.setLayerStack(layerStack);
- mDimSurface.setAlpha(0.0f);
- mDimSurface.show();
- } catch (Exception e) {
- Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
+ try {
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
+ "DimSurface",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
+ } else {
+ mDimSurface = new Surface(session, "DimSurface",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
+ if (WindowManagerService.SHOW_TRANSACTIONS ||
+ WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
+ " DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
+ mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
+ } catch (Exception e) {
+ Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
}
@@ -59,6 +59,11 @@
* Show the dim surface.
*/
void show(int dw, int dh, int layer, int color) {
+ if (mDimSurface == null) {
+ Slog.e(TAG, "show: no Surface");
+ return;
+ }
+
if (!mDimShown) {
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
dw + "x" + dh + " layer=" + layer + ")");
@@ -88,6 +93,11 @@
}
void hide() {
+ if (mDimSurface == null) {
+ Slog.e(TAG, "hide: no Surface");
+ return;
+ }
+
if (mDimShown) {
mDimShown = false;
try {
@@ -99,6 +109,13 @@
}
}
+ void kill() {
+ if (mDimSurface != null) {
+ mDimSurface.destroy();
+ mDimSurface = null;
+ }
+ }
+
public void printTo(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mDimSurface="); pw.println(mDimSurface);
pw.print(prefix); pw.print("mDimShown="); pw.print(mDimShown);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 377e89c..b67fb51 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -147,6 +147,22 @@
}
void removeDisplayLocked(final int displayId) {
+ final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
+ if (displayAnimator != null) {
+ if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
+ displayAnimator.mWindowAnimationBackgroundSurface.kill();
+ displayAnimator.mWindowAnimationBackgroundSurface = null;
+ }
+ if (displayAnimator.mScreenRotationAnimation != null) {
+ displayAnimator.mScreenRotationAnimation.kill();
+ displayAnimator.mScreenRotationAnimation = null;
+ }
+ if (displayAnimator.mDimAnimator != null) {
+ displayAnimator.mDimAnimator.kill();
+ displayAnimator.mDimAnimator = null;
+ }
+ }
+
mDisplayContentsAnimators.delete(displayId);
}
@@ -527,11 +543,15 @@
}
}
- windowAnimationBackgroundSurface.show(mDw, mDh,
- animLayer - WindowManagerService.LAYER_OFFSET_DIM,
- windowAnimationBackgroundColor);
+ if (windowAnimationBackgroundSurface != null) {
+ windowAnimationBackgroundSurface.show(mDw, mDh,
+ animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ windowAnimationBackgroundColor);
+ }
} else {
- windowAnimationBackgroundSurface.hide();
+ if (windowAnimationBackgroundSurface != null) {
+ windowAnimationBackgroundSurface.hide();
+ }
}
}
@@ -643,9 +663,8 @@
final DimAnimator.Parameters dimParams = displayAnimator.mDimParams;
final DimAnimator dimAnimator = displayAnimator.mDimAnimator;
- if (dimParams != null) {
- dimAnimator.updateParameters(
- mContext.getResources(), dimParams, mCurrentTime);
+ if (dimAnimator != null && dimParams != null) {
+ dimAnimator.updateParameters(mContext.getResources(), dimParams, mCurrentTime);
}
if (dimAnimator != null && dimAnimator.mDimShown) {
mAnimating |= dimAnimator.updateSurface(isDimmingLocked(displayId),
@@ -801,9 +820,9 @@
private class DisplayContentsAnimator {
WinAnimatorList mWinAnimators = new WinAnimatorList();
- final DimAnimator mDimAnimator;
+ DimAnimator mDimAnimator = null;
DimAnimator.Parameters mDimParams = null;
- final DimSurface mWindowAnimationBackgroundSurface;
+ DimSurface mWindowAnimationBackgroundSurface = null;
ScreenRotationAnimation mScreenRotationAnimation = null;
public DisplayContentsAnimator(int displayId) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index ae805c3..180579d 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3639,8 +3639,6 @@
if (wtoken != null) {
boolean delayed = false;
if (!wtoken.hidden) {
- wtoken.hidden = true;
-
final int N = wtoken.windows.size();
boolean changed = false;
@@ -3661,6 +3659,8 @@
}
}
+ wtoken.hidden = true;
+
if (changed) {
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
@@ -10870,8 +10870,8 @@
final DisplayContent displayContent = getDisplayContentLocked(displayId);
mDisplayContents.delete(displayId);
WindowList windows = displayContent.getWindowList();
- for (int i = windows.size() - 1; i >= 0; --i) {
- final WindowState win = windows.get(i);
+ while (!windows.isEmpty()) {
+ final WindowState win = windows.get(windows.size() - 1);
removeWindowLocked(win.mSession, win);
}
mAnimator.removeDisplayLocked(displayId);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
index a2e4298..0c6d41d 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
@@ -33,7 +33,7 @@
private int MAX_RADIUS = 25;
private ScriptC_threshold mScript;
- private int mRadius = MAX_RADIUS;
+ private float mRadius = MAX_RADIUS;
private float mSaturation = 1.0f;
private Allocation mScratchPixelsAllocation1;
private Allocation mScratchPixelsAllocation2;
@@ -51,13 +51,14 @@
public void onBar1Changed(int progress) {
- float fRadius = progress / 100.0f;
- fRadius *= (float)(MAX_RADIUS);
- mRadius = (int)fRadius;
+ mRadius = ((float)progress) / 100.0f * MAX_RADIUS;
+ if (mRadius <= 0.10f) {
+ mRadius = 0.10f;
+ }
if (mUseIntrinsic) {
mIntrinsic.setRadius(mRadius);
} else {
- mScript.invoke_setRadius(mRadius);
+ mScript.invoke_setRadius((int)mRadius);
}
}
@@ -111,7 +112,7 @@
if (mUseIntrinsic) {
mIntrinsic.setRadius(mRadius);
} else {
- mScript.invoke_setRadius(mRadius);
+ mScript.invoke_setRadius((int)mRadius);
}
}
}