Merge "Allow the configuration string to have quotes."
diff --git a/api/current.xml b/api/current.xml
index 3e8b9ac3..fd8a180 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -25564,6 +25564,17 @@
visibility="public"
>
</method>
+<method name="getCurrentModeType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNightMode"
return="int"
abstract="false"
@@ -25588,29 +25599,58 @@
<parameter name="mode" type="int">
</parameter>
</method>
-<field name="MODE_AUTO"
+<field name="ACTION_ENTER_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_ENTER_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NIGHT_AUTO"
type="int"
transient="false"
volatile="false"
- value="3"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="MODE_NIGHT"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MODE_NOTNIGHT"
+<field name="MODE_NIGHT_NO"
type="int"
transient="false"
volatile="false"
@@ -25621,6 +25661,17 @@
visibility="public"
>
</field>
+<field name="MODE_NIGHT_YES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="WallpaperInfo"
extends="java.lang.Object"
@@ -39068,17 +39119,6 @@
visibility="public"
>
</field>
-<field name="EXTRA_CAR_MODE_ENABLED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.extra.CAR_MODE_ENABLED""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="EXTRA_CC"
type="java.lang.String"
transient="false"
@@ -39233,17 +39273,6 @@
visibility="public"
>
</field>
-<field name="EXTRA_PHYSICAL_DOCK_STATE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.extra.PHYSICAL_DOCK_STATE""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="EXTRA_REMOTE_INTENT_TOKEN"
type="java.lang.String"
transient="false"
@@ -47911,6 +47940,17 @@
type="int"
transient="false"
volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UI_MODE_TYPE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
value="2"
static="true"
final="true"
@@ -175426,6 +175466,17 @@
visibility="public"
>
</method>
+<method name="getOverscrollMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPaddingBottom"
return="int"
abstract="false"
@@ -177591,6 +177642,19 @@
<parameter name="l" type="android.view.View.OnTouchListener">
</parameter>
</method>
+<method name="setOverscrollMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overscrollMode" type="int">
+</parameter>
+</method>
<method name="setPadding"
return="void"
abstract="false"
@@ -178213,6 +178277,39 @@
visibility="public"
>
</field>
+<field name="OVERSCROLL_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_IF_CONTENT_SCROLLS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
type="int[]"
transient="false"
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 6ac8a2a..bd637b7 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -33,6 +33,11 @@
void disableCarMode();
/**
+ * Return the current running mode.
+ */
+ int getCurrentModeType();
+
+ /**
* Sets the night mode.
* The mode can be one of:
* 1 - notnight mode
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 1ae9315..cb947b1 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -32,6 +32,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
+import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -80,7 +81,7 @@
private int mIconName1Col;
private int mIconName2Col;
private int mBackgroundColorCol;
-
+
static final int NONE = -1;
private final Runnable mStartSpinnerRunnable;
@@ -121,7 +122,7 @@
mSearchDialog.setWorking(false);
}
};
-
+
// delay 500ms when deleting
getFilter().setDelayer(new Filter.Delayer() {
@@ -129,7 +130,7 @@
public long getPostingDelay(CharSequence constraint) {
if (constraint == null) return 0;
-
+
long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
mPreviousLength = constraint.length();
return delay;
@@ -203,15 +204,16 @@
}
private void updateSpinnerState(Cursor cursor) {
+ Bundle extras = cursor != null ? cursor.getExtras() : null;
if (DBG) {
Log.d(LOG_TAG, "updateSpinnerState - extra = "
- + (cursor != null
- ? cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+ + (extras != null
+ ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
: null));
}
// Check if the Cursor indicates that the query is not complete and show the spinner
- if (cursor != null
- && cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+ if (extras != null
+ && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
return;
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index aca8ab4..defe421 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -1,5 +1,7 @@
package android.app;
+import android.content.Context;
+import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -9,6 +11,22 @@
* allow applications to control UI modes of the device.
* It provides functionality to disable the car mode and it gives access to the
* night mode settings.
+ *
+ * <p>These facilities are built on top of the underlying
+ * {@link android.content.Intent#ACTION_DOCK_EVENT} broadcasts that are sent when the user
+ * physical places the device into and out of a dock. When that happens,
+ * the UiModeManager switches the system {@link android.content.res.Configuration}
+ * to the appropriate UI mode, sends broadcasts about the mode switch, and
+ * starts the corresponding mode activity if appropriate. See the
+ * broadcasts {@link #ACTION_ENTER_CAR_MODE} and
+ * {@link #ACTION_ENTER_DESK_MODE} for more information.
+ *
+ * <p>In addition, the user may manually switch the system to car mode without
+ * physically being in a dock. While in car mode -- whether by manual action
+ * from the user or being physically placed in a dock -- a notification is
+ * displayed allowing the user to exit dock mode. Thus the dock mode
+ * represented here may be different than the current state of the underlying
+ * dock event broadcast.
*
* <p>You do not instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService
@@ -17,19 +35,72 @@
public class UiModeManager {
private static final String TAG = "UiModeManager";
- public static final int MODE_NOTNIGHT = 1;
- public static final int MODE_NIGHT = 2;
- public static final int MODE_AUTO = 3;
+ /**
+ * Broadcast sent when the device's UI has switched to car mode, either
+ * by being placed in a car dock or explicit action of the user. After
+ * sending the broadcast, the system will start the intent
+ * {@link android.content.Intent#ACTION_MAIN} with category
+ * {@link android.content.Intent#CATEGORY_CAR_DOCK}
+ * to display the car UI, which typically what an application would
+ * implement to provide their own interface. However, applications can
+ * also monitor this Intent in order to be informed of mode changes or
+ * prevent the normal car UI from being displayed by setting the result
+ * of the broadcast to {@link Activity#RESULT_CANCELED}.
+ */
+ public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switch away from car mode back
+ * to normal mode. Typically used by a car mode app, to dismiss itself
+ * when the user exits car mode.
+ */
+ public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switched to desk mode,
+ * by being placed in a desk dock. After
+ * sending the broadcast, the system will start the intent
+ * {@link android.content.Intent#ACTION_MAIN} with category
+ * {@link android.content.Intent#CATEGORY_DESK_DOCK}
+ * to display the desk UI, which typically what an application would
+ * implement to provide their own interface. However, applications can
+ * also monitor this Intent in order to be informed of mode changes or
+ * prevent the normal desk UI from being displayed by setting the result
+ * of the broadcast to {@link Activity#RESULT_CANCELED}.
+ */
+ public static String ACTION_ENTER_DESK_MODE = "android.app.action.ENTER_DESK_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switch away from car mode back
+ * to normal mode. Typically used by a car mode app, to dismiss itself
+ * when the user exits car mode.
+ */
+ public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * automatically switch night mode on and off based on the time.
+ */
+ public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * never run in night mode.
+ */
+ public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * always run in night mode.
+ */
+ public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
private IUiModeManager mService;
/*package*/ UiModeManager() {
mService = IUiModeManager.Stub.asInterface(
- ServiceManager.getService("uimode"));
+ ServiceManager.getService(Context.UI_MODE_SERVICE));
}
/**
- * Disables the car mode.
+ * Turn off special mode if currently in car mode.
*/
public void disableCarMode() {
if (mService != null) {
@@ -42,17 +113,35 @@
}
/**
+ * Return the current running mode type. May be one of
+ * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
+ * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
+ * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+ */
+ public int getCurrentModeType() {
+ if (mService != null) {
+ try {
+ return mService.getCurrentModeType();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getCurrentModeType: RemoteException", e);
+ }
+ }
+ return Configuration.UI_MODE_TYPE_NORMAL;
+ }
+
+ /**
* Sets the night mode. Changes to the night mode are only effective when
- * the car mode is enabled on a device.
+ * the car or desk mode is enabled on a device.
*
* <p>The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NOTNIGHT}<em> - sets the device into notnight
+ * <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
* mode.</li>
- * <li><em>{@link #MODE_NIGHT}</em> - sets the device into night mode.
+ * <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
* </li>
- * <li><em>{@link #MODE_AUTO}</em> - automatic night/notnight switching
+ * <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
* depending on the location and certain other sensors.</li>
+ * </ul>
*/
public void setNightMode(int mode) {
if (mService != null) {
@@ -67,8 +156,8 @@
/**
* Returns the currently configured night mode.
*
- * @return {@link #MODE_NOTNIGHT}, {@link #MODE_NIGHT} or {@link #MODE_AUTO}
- * When an error occurred -1 is returned.
+ * @return {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, or
+ * {@link #MODE_NIGHT_AUTO}. When an error occurred -1 is returned.
*/
public int getNightMode() {
if (mService != null) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 91b1c4e..5fb2aae 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -29,6 +29,7 @@
import android.database.SQLException;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -217,6 +218,13 @@
return ContentProvider.this.openAssetFile(uri, mode);
}
+ /**
+ * @hide
+ */
+ public Bundle call(String method, String request, Bundle args) {
+ return ContentProvider.this.call(method, request, args);
+ }
+
private void enforceReadPermission(Uri uri) {
final int uid = Binder.getCallingUid();
if (uid == mMyUid) {
@@ -748,4 +756,18 @@
}
return results;
}
-}
\ No newline at end of file
+
+ /**
+ * @hide -- until interface has proven itself
+ *
+ * Call an provider-defined method. This can be used to implement
+ * interfaces that are cheaper than using a Cursor.
+ *
+ * @param method Method name to call. Opaque to framework.
+ * @param request Nullable String argument passed to method.
+ * @param args Nullable Bundle argument passed to method.
+ */
+ public Bundle call(String method, String request, Bundle args) {
+ return null;
+ }
+}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index bacb684..81b8055 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -26,6 +26,7 @@
import android.database.IContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
@@ -222,6 +223,21 @@
}
return true;
}
+
+ case CALL_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+
+ String method = data.readString();
+ String stringArg = data.readString();
+ Bundle args = data.readBundle();
+
+ Bundle responseBundle = call(method, stringArg, args);
+
+ reply.writeNoException();
+ reply.writeBundle(responseBundle);
+ return true;
+ }
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -485,6 +501,22 @@
return fd;
}
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ data.writeString(method);
+ data.writeString(request);
+ data.writeBundle(args);
+
+ mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ return reply.readBundle();
+ }
+
private IBinder mRemote;
}
-
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 1b0ef34..bcef75e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -736,7 +736,7 @@
* @hide
*/
public final IContentProvider acquireProvider(String name) {
- if(name == null) {
+ if (name == null) {
return null;
}
return acquireProvider(mContext, name);
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 1b0ca58..67e7581 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -22,10 +22,11 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
-import android.os.RemoteException;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -58,6 +59,17 @@
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
+ /**
+ * @hide -- until interface has proven itself
+ *
+ * Call an provider-defined method. This can be used to implement
+ * interfaces that are cheaper than using a Cursor.
+ *
+ * @param method Method name to call. Opaque to framework.
+ * @param request Nullable String argument passed to method.
+ * @param args Nullable Bundle argument passed to method.
+ */
+ public Bundle call(String method, String request, Bundle args) throws RemoteException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
@@ -71,4 +83,5 @@
static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
+ static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e63851f..fd5591d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1838,21 +1838,17 @@
"android.intent.action.REBOOT";
/**
- * Broadcast Action: A sticky broadcast indicating the phone was docked
- * or undocked.
+ * Broadcast Action: A sticky broadcast for changes in the physical
+ * docking state of the device.
*
* <p>The intent will have the following extra values:
* <ul>
* <li><em>{@link #EXTRA_DOCK_STATE}</em> - the current dock
- * state, which depends on the state of the car mode.</li>
- * <li><em>{@link #EXTRA_PHYSICAL_DOCK_STATE}</em> - the physical dock
- * state.</li>
- * <li><em>{@link #EXTRA_CAR_MODE_ENABLED}</em> - a boolean indicating the
- * state of the car mode.</li>
+ * state, indicating which dock the device is physically in.</li>
* </ul>
- * <p>This is intended for monitoring the current dock state.
- * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK}
- * or {@link #CATEGORY_DESK_DOCK} instead.
+ * <p>This is intended for monitoring the current physical dock state.
+ * See {@link android.app.UiModeManager} for the normal API dealing with
+ * dock mode changes.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DOCK_EVENT =
@@ -2014,15 +2010,15 @@
"android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST";
/**
* An activity to run when device is inserted into a car dock.
- * Used with {@link #ACTION_MAIN} to launch an activity.
- * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+ * Used with {@link #ACTION_MAIN} to launch an activity. For more
+ * information, see {@link android.app.UiModeManager}.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
/**
* An activity to run when device is inserted into a car dock.
- * Used with {@link #ACTION_MAIN} to launch an activity.
- * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+ * Used with {@link #ACTION_MAIN} to launch an activity. For more
+ * information, see {@link android.app.UiModeManager}.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
@@ -2191,22 +2187,6 @@
public static final int EXTRA_DOCK_STATE_CAR = 2;
/**
- * Used as an int extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
- * intents to request the physical dock state. Possible values are
- * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
- * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
- * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
- */
- public static final String EXTRA_PHYSICAL_DOCK_STATE =
- "android.intent.extra.PHYSICAL_DOCK_STATE";
-
- /**
- * Used as an boolean extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
- * intents to indicate that the car mode is enabled or not.
- */
- public static final String EXTRA_CAR_MODE_ENABLED = "android.intent.extra.CAR_MODE_ENABLED";
-
- /**
* Boolean that can be supplied as meta-data with a dock activity, to
* indicate that the dock should take over the home key when it is active.
*/
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index a737283..61e3004 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -165,7 +165,8 @@
public static final int UI_MODE_TYPE_MASK = 0x0f;
public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
public static final int UI_MODE_TYPE_NORMAL = 0x01;
- public static final int UI_MODE_TYPE_CAR = 0x02;
+ public static final int UI_MODE_TYPE_DESK = 0x02;
+ public static final int UI_MODE_TYPE_CAR = 0x03;
public static final int UI_MODE_NIGHT_MASK = 0x30;
public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
@@ -175,11 +176,12 @@
/**
* Bit mask of the ui mode. Currently there are two fields:
* <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
- * device. They may be one of
- * {@link #UI_MODE_TYPE_NORMAL} or {@link #UI_MODE_TYPE_CAR}.
+ * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
+ * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
+ * or {@link #UI_MODE_TYPE_CAR}.
*
* <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
- * is in a special mode. They may be one of
+ * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
* {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
*/
public int uiMode;
@@ -272,7 +274,7 @@
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
- uiMode = UI_MODE_TYPE_NORMAL;
+ uiMode = UI_MODE_TYPE_UNDEFINED;
seq = 0;
}
@@ -354,10 +356,17 @@
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
screenLayout = delta.screenLayout;
}
- if (delta.uiMode != UI_MODE_TYPE_NORMAL
+ if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
- uiMode = delta.uiMode;
+ if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
+ uiMode = (uiMode&~UI_MODE_TYPE_MASK)
+ | (delta.uiMode&UI_MODE_TYPE_MASK);
+ }
+ if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
+ uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
+ | (delta.uiMode&UI_MODE_NIGHT_MASK);
+ }
}
if (delta.seq != 0) {
@@ -439,7 +448,7 @@
&& screenLayout != delta.screenLayout) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
- if (delta.uiMode != UI_MODE_TYPE_NORMAL
+ if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5d5bd9c..8c8d3e5 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2025,7 +2025,10 @@
InputConnection ic = getCurrentInputConnection();
mExtractedText = ic == null? null
: ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
-
+ if (mExtractedText == null || ic == null) {
+ Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
+ + mExtractedText + ", input connection = " + ic);
+ }
final EditorInfo ei = getCurrentInputEditorInfo();
try {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index d6fe107..0ec1c74 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -132,6 +132,45 @@
}
/**
+ * Make a Bundle for a single key/value pair.
+ *
+ * @hide
+ */
+ public static Bundle forPair(String key, String value) {
+ // TODO: optimize this case.
+ Bundle b = new Bundle(1);
+ b.putString(key, value);
+ return b;
+ }
+
+ /**
+ * TODO: optimize this later (getting just the value part of a Bundle
+ * with a single pair) once Bundle.forPair() above is implemented
+ * with a special single-value Map implementation/serialization.
+ *
+ * Note: value in single-pair Bundle may be null.
+ *
+ * @hide
+ */
+ public String getPairValue() {
+ unparcel();
+ int size = mMap.size();
+ if (size > 1) {
+ Log.w(LOG_TAG, "getPairValue() used on Bundle with multiple pairs.");
+ }
+ if (size == 0) {
+ return null;
+ }
+ Object o = mMap.values().iterator().next();
+ try {
+ return (String) o;
+ } catch (ClassCastException e) {
+ typeWarning("getPairValue()", o, "String", e);
+ return null;
+ }
+ }
+
+ /**
* Changes the ClassLoader this Bundle uses when instantiating objects.
*
* @param loader An explicit ClassLoader to use when instantiating objects
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7df509f..726f98a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -27,6 +27,7 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -492,6 +493,16 @@
// End of Intent actions for Settings
/**
+ * @hide - Private call() method on SettingsProvider to read from 'system' table.
+ */
+ public static final String CALL_METHOD_GET_SYSTEM = "GET_system";
+
+ /**
+ * @hide - Private call() method on SettingsProvider to read from 'secure' table.
+ */
+ public static final String CALL_METHOD_GET_SECURE = "GET_secure";
+
+ /**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
* This can be passed as an extra field in an Activity Intent with one or more syncable content
@@ -544,23 +555,36 @@
}
}
+ // Thread-safe.
private static class NameValueCache {
private final String mVersionSystemProperty;
private final Uri mUri;
- // Must synchronize(mValues) to access mValues and mValuesVersion.
+ private static final String[] SELECT_VALUE =
+ new String[] { Settings.NameValueTable.VALUE };
+ private static final String NAME_EQ_PLACEHOLDER = "name=?";
+
+ // Must synchronize on 'this' to access mValues and mValuesVersion.
private final HashMap<String, String> mValues = new HashMap<String, String>();
private long mValuesVersion = 0;
- public NameValueCache(String versionSystemProperty, Uri uri) {
+ // Initially null; set lazily and held forever. Synchronized on 'this'.
+ private IContentProvider mContentProvider = null;
+
+ // The method we'll call (or null, to not use) on the provider
+ // for the fast path of retrieving settings.
+ private final String mCallCommand;
+
+ public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) {
mVersionSystemProperty = versionSystemProperty;
mUri = uri;
+ mCallCommand = callCommand;
}
public String getString(ContentResolver cr, String name) {
long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
- synchronized (mValues) {
+ synchronized (this) {
if (mValuesVersion != newValuesVersion) {
if (LOCAL_LOGV) {
Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " +
@@ -576,17 +600,47 @@
}
}
+ IContentProvider cp = null;
+ synchronized (this) {
+ cp = mContentProvider;
+ if (cp == null) {
+ cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
+ }
+ }
+
+ // Try the fast path first, not using query(). If this
+ // fails (alternate Settings provider that doesn't support
+ // this interface?) then we fall back to the query/table
+ // interface.
+ if (mCallCommand != null) {
+ try {
+ Bundle b = cp.call(mCallCommand, name, null);
+ if (b != null) {
+ String value = b.getPairValue();
+ synchronized (this) {
+ mValues.put(name, value);
+ }
+ return value;
+ }
+ // If the response Bundle is null, we fall through
+ // to the query interface below.
+ } catch (RemoteException e) {
+ // Not supported by the remote side? Fall through
+ // to query().
+ }
+ }
+
Cursor c = null;
try {
- c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE },
- Settings.NameValueTable.NAME + "=?", new String[]{name}, null);
+ c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
+ new String[]{name}, null);
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + mUri);
return null;
}
String value = c.moveToNext() ? c.getString(0) : null;
- synchronized (mValues) {
+ synchronized (this) {
mValues.put(name, value);
}
if (LOCAL_LOGV) {
@@ -594,7 +648,7 @@
name + " = " + (value == null ? "(null)" : value));
}
return value;
- } catch (SQLException e) {
+ } catch (RemoteException e) {
Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
return null; // Return null, but don't cache it.
} finally {
@@ -611,7 +665,8 @@
public static final class System extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
- private static volatile NameValueCache mNameValueCache = null;
+ // Populated lazily, guarded by class object:
+ private static NameValueCache sNameValueCache = null;
private static final HashSet<String> MOVED_TO_SECURE;
static {
@@ -660,10 +715,11 @@
+ " to android.provider.Settings.Secure, returning read-only value.");
return Secure.getString(resolver, name);
}
- if (mNameValueCache == null) {
- mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+ CALL_METHOD_GET_SYSTEM);
}
- return mNameValueCache.getString(resolver, name);
+ return sNameValueCache.getString(resolver, name);
}
/**
@@ -1905,7 +1961,8 @@
public static final class Secure extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
- private static volatile NameValueCache mNameValueCache = null;
+ // Populated lazily, guarded by class object:
+ private static NameValueCache sNameValueCache = null;
/**
* Look up a name in the database.
@@ -1914,10 +1971,11 @@
* @return the corresponding value, or null if not present
*/
public synchronized static String getString(ContentResolver resolver, String name) {
- if (mNameValueCache == null) {
- mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+ CALL_METHOD_GET_SECURE);
}
- return mNameValueCache.getString(resolver, name);
+ return sNameValueCache.getString(resolver, name);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index abbab0e..7a0c445 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1516,19 +1516,28 @@
/**
* Always allow a user to overscroll this view, provided it is a
* view that can scroll.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_ALWAYS = 0;
+ public static final int OVERSCROLL_ALWAYS = 0;
/**
* Allow a user to overscroll this view only if the content is large
* enough to meaningfully scroll, provided it is a view that can scroll.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
+ public static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
/**
* Never allow a user to overscroll this view.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_NEVER = 2;
+ public static final int OVERSCROLL_NEVER = 2;
/**
* Controls the overscroll mode for this view.
@@ -8770,6 +8779,38 @@
boolean clampedX, boolean clampedY) {
// Intentionally empty.
}
+
+ /**
+ * Returns the overscroll mode for this view. The result will be
+ * one of {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+ * (allow overscrolling only if the view content is larger than the container),
+ * or {@link #OVERSCROLL_NEVER}.
+ *
+ * @return This view's overscroll mode.
+ */
+ public int getOverscrollMode() {
+ return mOverscrollMode;
+ }
+
+ /**
+ * Set the overscroll mode for this view. Valid overscroll modes are
+ * {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+ * (allow overscrolling only if the view content is larger than the container),
+ * or {@link #OVERSCROLL_NEVER}.
+ *
+ * Setting the overscroll mode of a view will have an effect only if the
+ * view is capable of scrolling.
+ *
+ * @param overscrollMode The new overscroll mode for this view.
+ */
+ public void setOverscrollMode(int overscrollMode) {
+ if (overscrollMode != OVERSCROLL_ALWAYS &&
+ overscrollMode != OVERSCROLL_IF_CONTENT_SCROLLS &&
+ overscrollMode != OVERSCROLL_NEVER) {
+ throw new IllegalArgumentException("Invalid overscroll mode " + overscrollMode);
+ }
+ mOverscrollMode = overscrollMode;
+ }
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6a95831..b8d71b9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -24,12 +24,17 @@
import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Interpolator;
+import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
import android.net.Uri;
@@ -70,7 +75,7 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import android.widget.ZoomControls;
@@ -455,7 +460,9 @@
// time for the longest scroll animation
private static final int MAX_DURATION = 750; // milliseconds
private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
- private Scroller mScroller;
+ private OverScroller mScroller;
+ private boolean mInOverScrollMode = false;
+ private static Paint mOverScrollBackground;
private boolean mWrapContent;
private static final int MOTIONLESS_FALSE = 0;
@@ -810,7 +817,7 @@
mViewManager = new ViewManager(this);
mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
mDatabase = WebViewDatabase.getInstance(context);
- mScroller = new Scroller(context);
+ mScroller = new OverScroller(context);
mZoomButtonsController = new ZoomButtonsController(this);
mZoomButtonsController.setOnZoomListener(mZoomListener);
@@ -1024,7 +1031,8 @@
* Return the amount of the titlebarview (if any) that is visible
*/
private int getVisibleTitleHeight() {
- return Math.max(getTitleHeight() - mScrollY, 0);
+ // need to restrict mScrollY due to over scroll
+ return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
}
/*
@@ -1867,11 +1875,13 @@
// Expects x in view coordinates
private int pinLocX(int x) {
+ if (mInOverScrollMode) return x;
return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
}
// Expects y in view coordinates
private int pinLocY(int y) {
+ if (mInOverScrollMode) return y;
int titleH = getTitleHeight();
// if the titlebar is still visible, just pin against 0
if (y <= titleH) {
@@ -2269,6 +2279,24 @@
scrollBar.draw(canvas);
}
+ @Override
+ protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
+ boolean clampedY) {
+ mInOverScrollMode = false;
+ int maxX = computeMaxScrollX();
+ if (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f && maxX == 0) {
+ // do not over scroll x if the page can't be zoomed and it just fits
+ // the screen
+ scrollX = pinLocX(scrollX);
+ } else if (scrollX < 0 || scrollX > maxX) {
+ mInOverScrollMode = true;
+ }
+ if (scrollY < 0 || scrollY > computeMaxScrollY()) {
+ mInOverScrollMode = true;
+ }
+ super.scrollTo(scrollX, scrollY);
+ }
+
/**
* Get the url for the current page. This is not always the same as the url
* passed to WebViewClient.onPageStarted because although the load for
@@ -2611,13 +2639,14 @@
if (mScroller.computeScrollOffset()) {
int oldX = mScrollX;
int oldY = mScrollY;
- mScrollX = mScroller.getCurrX();
- mScrollY = mScroller.getCurrY();
+ int x = mScroller.getCurrX();
+ int y = mScroller.getCurrY();
postInvalidate(); // So we draw again
- if (oldX != mScrollX || oldY != mScrollY) {
- // as onScrollChanged() is not called, sendOurVisibleRect()
- // needs to be call explicitly
- sendOurVisibleRect();
+ if (oldX != x || oldY != y) {
+ overscrollBy(x - oldX, y - oldY, oldX, oldY,
+ computeMaxScrollX(), computeMaxScrollY(),
+ getViewWidth() / 3, getViewHeight() / 3);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
} else {
super.computeScroll();
@@ -3028,8 +3057,13 @@
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child == mTitleBar) {
// When drawing the title bar, move it horizontally to always show
- // at the top of the WebView.
+ // at the top of the WebView. While overscroll, stick the title bar
+ // on the top otherwise we may have two during loading, one is drawn
+ // here, another is drawn by the Browser.
mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
+ if (mScrollY <= 0) {
+ mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
+ }
}
return super.drawChild(canvas, child, drawingTime);
}
@@ -3057,6 +3091,29 @@
}
int saveCount = canvas.save();
+ if (mInOverScrollMode) {
+ if (mOverScrollBackground == null) {
+ mOverScrollBackground = new Paint();
+ Bitmap bm = BitmapFactory.decodeResource(
+ mContext.getResources(),
+ com.android.internal.R.drawable.pattern_underwear);
+ mOverScrollBackground.setShader(new BitmapShader(bm,
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ }
+ int top = getTitleHeight();
+ // first draw the background and anchor to the top of the view
+ canvas.save();
+ canvas.translate(mScrollX, mScrollY);
+ canvas.clipRect(-mScrollX, top - mScrollY,
+ computeHorizontalScrollRange() - mScrollX, top
+ + computeVerticalScrollRange() - mScrollY,
+ Region.Op.DIFFERENCE);
+ canvas.drawPaint(mOverScrollBackground);
+ canvas.restore();
+ // next clip the region for the content
+ canvas.clipRect(0, top, computeHorizontalScrollRange(), top
+ + computeVerticalScrollRange());
+ }
if (mTitleBar != null) {
canvas.translate(0, (int) mTitleBar.getHeight());
}
@@ -3074,12 +3131,12 @@
canvas.restoreToCount(saveCount);
// Now draw the shadow.
- if (mTitleBar != null) {
- int y = mScrollY + getVisibleTitleHeight();
+ int titleH = getVisibleTitleHeight();
+ if (mTitleBar != null && titleH == 0) {
int height = (int) (5f * getContext().getResources()
.getDisplayMetrics().density);
- mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
- y + height);
+ mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
+ mScrollY + height);
mTitleShadow.draw(canvas);
}
if (AUTO_REDRAW_HACK && mAutoRedraw) {
@@ -4680,18 +4737,6 @@
}
// do pan
- int newScrollX = pinLocX(mScrollX + deltaX);
- int newDeltaX = newScrollX - mScrollX;
- if (deltaX != newDeltaX) {
- deltaX = newDeltaX;
- fDeltaX = (float) newDeltaX;
- }
- int newScrollY = pinLocY(mScrollY + deltaY);
- int newDeltaY = newScrollY - mScrollY;
- if (deltaY != newDeltaY) {
- deltaY = newDeltaY;
- fDeltaY = (float) newDeltaY;
- }
boolean done = false;
boolean keepScrollBarsVisible = false;
if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -4736,7 +4781,9 @@
}
}
if ((deltaX | deltaY) != 0) {
- scrollBy(deltaX, deltaY);
+ overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
+ computeMaxScrollX(), computeMaxScrollY(),
+ getViewWidth() / 3, getViewHeight() / 3);
if (deltaX != 0) {
mLastTouchX = x;
}
@@ -4819,8 +4866,8 @@
Log.w(LOGTAG, "Miss a drag as we are waiting for" +
" WebCore's response for touch down.");
if (mFullScreenHolder == null
- && (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
- || computeVerticalScrollExtent() < computeVerticalScrollRange())) {
+ && (computeMaxScrollX() > 0
+ || computeMaxScrollY() > 0)) {
// remove the pending TOUCH_EVENT and send a
// cancel
mWebViewCore
@@ -4866,6 +4913,12 @@
mVelocityTracker.addMovement(ev);
doFling();
break;
+ } else {
+ if (mScroller.springback(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0,
+ computeMaxScrollY())) {
+ invalidate();
+ }
}
mLastVelocity = 0;
WebViewCore.resumePriority();
@@ -4886,6 +4939,12 @@
}
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
+ if (mTouchMode == TOUCH_DRAG_MODE) {
+ if (mScroller.springback(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0, computeMaxScrollY())) {
+ invalidate();
+ }
+ }
break;
}
}
@@ -5204,16 +5263,18 @@
}
}
+ private int computeMaxScrollX() {
+ return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ }
+
private int computeMaxScrollY() {
- int maxContentH = computeVerticalScrollRange() + getTitleHeight();
- return Math.max(maxContentH - getViewHeightWithTitle(), getTitleHeight());
+ return Math.max(computeVerticalScrollRange() + getTitleHeight()
+ - getViewHeightWithTitle(), getTitleHeight());
}
public void flingScroll(int vx, int vy) {
- int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
- int maxY = computeMaxScrollY();
-
- mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
+ mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
+ computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
invalidate();
}
@@ -5221,7 +5282,7 @@
if (mVelocityTracker == null) {
return;
}
- int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ int maxX = computeMaxScrollX();
int maxY = computeMaxScrollY();
mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
@@ -5243,6 +5304,10 @@
}
if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
WebViewCore.resumePriority();
+ if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
+ 0, computeMaxScrollY())) {
+ invalidate();
+ }
return;
}
float currentVelocity = mScroller.getCurrVelocity();
@@ -5270,7 +5335,8 @@
mLastVelY = vy;
mLastVelocity = (float) Math.hypot(vx, vy);
- mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
+ mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
+ getViewWidth() / 3, getViewHeight() / 3);
// TODO: duration is calculated based on velocity, if the range is
// small, the animation will stop before duration is up. We may
// want to calculate how long the animation is going to run to precisely
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a41d25b..f1e614b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -52,7 +52,13 @@
<protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
+ <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
+
<protected-broadcast android:name="android.backup.intent.RUN" />
<protected-broadcast android:name="android.backup.intent.CLEAR" />
<protected-broadcast android:name="android.backup.intent.INIT" />
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
new file mode 100644
index 0000000..651212f
--- /dev/null
+++ b/core/res/res/drawable/pattern_underwear.png
Binary files differ
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index cbcef4e..0e796dc 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -946,7 +946,8 @@
MASK_UI_MODE_TYPE = 0x0f,
UI_MODE_TYPE_ANY = 0x00,
UI_MODE_TYPE_NORMAL = 0x01,
- UI_MODE_TYPE_CAR = 0x02,
+ UI_MODE_TYPE_DESK = 0x02,
+ UI_MODE_TYPE_CAR = 0x03,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 2269352..8089389 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -3247,7 +3247,10 @@
if (mBytesRead < 0) {
LOGE("Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
- sleep(1);
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
}
mRsmpInIndex = mFrameCount;
framesOut = 0;
@@ -3429,7 +3432,10 @@
if (mBytesRead < 0) {
LOGE("RecordThread::getNextBuffer() Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
- sleep(1);
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
}
buffer->raw = 0;
buffer->frameCount = 0;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index bce3371..ad037d6 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -552,13 +552,17 @@
audioBuffer.frameCount = userSize/frameSize();
- // Calling obtainBuffer() with a negative wait count causes
- // an (almost) infinite wait time.
- status_t err = obtainBuffer(&audioBuffer, -1);
+ // By using a wait count corresponding to twice the timeout period in
+ // obtainBuffer() we give a chance to recover once for a read timeout
+ // (if media_server crashed for instance) before returning a length of
+ // 0 bytes read to the client
+ status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS));
if (err < 0) {
// out of buffers, return #bytes written
if (err == status_t(NO_MORE_BUFFERS))
break;
+ if (err == status_t(TIMED_OUT))
+ err = 0;
return ssize_t(err);
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ab65b44..5090c39 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -297,13 +297,11 @@
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
- if (setVideoSource(extractor->getTrack(i)) == OK) {
- haveVideo = true;
- }
+ setVideoSource(extractor->getTrack(i));
+ haveVideo = true;
} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
- if (setAudioSource(extractor->getTrack(i)) == OK) {
- haveAudio = true;
- }
+ setAudioSource(extractor->getTrack(i));
+ haveAudio = true;
}
if (haveAudio && haveVideo) {
@@ -331,6 +329,9 @@
}
mPrefetcher.clear();
+ mAudioTrack.clear();
+ mVideoTrack.clear();
+
// Shutdown audio first, so that the respone to the reset request
// appears to happen instantaneously as far as the user is concerned
// If we did this later, audio would continue playing while we
@@ -699,32 +700,34 @@
return OK;
}
-status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {
- if (source == NULL) {
- return UNKNOWN_ERROR;
- }
+void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
if (mPrefetcher != NULL) {
source = mPrefetcher->addSource(source);
}
- sp<MetaData> meta = source->getFormat();
+ mAudioTrack = source;
+}
+
+status_t AwesomePlayer::initAudioDecoder() {
+ sp<MetaData> meta = mAudioTrack->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
- mAudioSource = source;
+ mAudioSource = mAudioTrack;
} else {
mAudioSource = OMXCodec::Create(
- mClient.interface(), source->getFormat(),
+ mClient.interface(), mAudioTrack->getFormat(),
false, // createEncoder
- source);
+ mAudioTrack);
}
if (mAudioSource != NULL) {
int64_t durationUs;
- if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (mDurationUs < 0 || durationUs > mDurationUs) {
mDurationUs = durationUs;
}
@@ -734,30 +737,32 @@
return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
}
-status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
- if (source == NULL) {
- return UNKNOWN_ERROR;
- }
+void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
if (mPrefetcher != NULL) {
source = mPrefetcher->addSource(source);
}
+ mVideoTrack = source;
+}
+
+status_t AwesomePlayer::initVideoDecoder() {
mVideoSource = OMXCodec::Create(
- mClient.interface(), source->getFormat(),
+ mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
- source);
+ mVideoTrack);
if (mVideoSource != NULL) {
int64_t durationUs;
- if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (mDurationUs < 0 || durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
- CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
- CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
+ CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
+ CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
mVideoSource->start();
}
@@ -1045,6 +1050,19 @@
return setDataSource_l(extractor);
}
+void AwesomePlayer::abortPrepare(status_t err) {
+ CHECK(err != OK);
+
+ if (mIsAsyncPrepare) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ }
+
+ mPrepareResult = err;
+ mFlags &= ~PREPARING;
+ mAsyncPrepareEvent = NULL;
+ mPreparedCondition.broadcast();
+}
+
void AwesomePlayer::onPrepareAsyncEvent() {
{
Mutex::Autolock autoLock(mLock);
@@ -1053,15 +1071,7 @@
status_t err = finishSetDataSource_l();
if (err != OK) {
- if (mIsAsyncPrepare) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
- }
-
- mPrepareResult = err;
- mFlags &= ~PREPARING;
- mAsyncPrepareEvent = NULL;
- mPreparedCondition.broadcast();
-
+ abortPrepare(err);
return;
}
}
@@ -1081,6 +1091,24 @@
Mutex::Autolock autoLock(mLock);
+ if (mVideoTrack != NULL && mVideoSource == NULL) {
+ status_t err = initVideoDecoder();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ if (mAudioTrack != NULL && mAudioSource == NULL) {
+ status_t err = initAudioDecoder();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
if (mIsAsyncPrepare) {
if (mVideoWidth < 0 || mVideoHeight < 0) {
notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 3590987..7106524 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -112,10 +112,12 @@
sp<DataSource> mFileSource;
+ sp<MediaSource> mVideoTrack;
sp<MediaSource> mVideoSource;
sp<AwesomeRenderer> mVideoRenderer;
bool mVideoRendererIsPreview;
+ sp<MediaSource> mAudioTrack;
sp<MediaSource> mAudioSource;
AudioPlayer *mAudioPlayer;
int64_t mDurationUs;
@@ -199,8 +201,11 @@
void cancelPlayerEvents(bool keepBufferingGoing = false);
- status_t setAudioSource(sp<MediaSource> source);
- status_t setVideoSource(sp<MediaSource> source);
+ void setAudioSource(sp<MediaSource> source);
+ status_t initAudioDecoder();
+
+ void setVideoSource(sp<MediaSource> source);
+ status_t initVideoDecoder();
void onStreamDone();
@@ -210,6 +215,8 @@
void onBufferingUpdate();
void onCheckAudioStatus();
void onPrepareAsyncEvent();
+ void abortPrepare(status_t err);
+
status_t finishSetDataSource_l();
AwesomePlayer(const AwesomePlayer &);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db802d3..4f1146b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -30,9 +30,11 @@
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQueryBuilder;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.provider.DrmStore;
@@ -48,6 +50,8 @@
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_OLD_FAVORITES = "old_favorites";
+ private static final String[] COLUMN_VALUE = new String[] { "value" };
+
protected DatabaseHelper mOpenHelper;
private BackupManager mBackupManager;
@@ -220,6 +224,44 @@
}
}
+ /**
+ * Fast path that avoids the use of chatty remoted Cursors.
+ */
+ @Override
+ public Bundle call(String method, String request, Bundle args) {
+ if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
+ return lookupValue("system", request);
+ }
+
+ if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
+ return lookupValue("secure", request);
+ }
+ return null;
+ }
+
+ // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
+ // possibly with a null value, or null on failure.
+ private Bundle lookupValue(String table, String key) {
+ // TODO: avoid database lookup and serve from in-process cache.
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor cursor = null;
+ try {
+ cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
+ null, null, null, null);
+ if (cursor != null && cursor.getCount() == 1) {
+ cursor.moveToFirst();
+ String value = cursor.getString(0);
+ return Bundle.forPair("value", value);
+ }
+ } catch (SQLiteException e) {
+ Log.w(TAG, "settings lookup error", e);
+ return null;
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ return Bundle.forPair("value", null);
+ }
+
@Override
public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
SqlArguments args = new SqlArguments(url, where, whereArgs);
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 28236f0..bee8872 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -16,53 +16,24 @@
package com.android.server;
-import android.app.Activity;
-import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
-import android.app.IActivityManager;
-import android.app.IUiModeManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Binder;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.provider.Settings;
import android.server.BluetoothService;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.app.DisableCarModeActivity;
-import com.android.internal.widget.LockPatternUtils;
-
import java.io.FileNotFoundException;
import java.io.FileReader;
@@ -76,187 +47,22 @@
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
- private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
-
private static final int MSG_DOCK_STATE = 0;
- private static final int MSG_UPDATE_TWILIGHT = 1;
- private static final int MSG_ENABLE_LOCATION_UPDATES = 2;
-
- public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_MASK >> 4;
- public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
- public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
-
- private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
- private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
- private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
-
- private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
- private int mNightMode = MODE_NIGHT_NO;
- private boolean mCarModeEnabled = false;
-
private boolean mSystemReady;
private final Context mContext;
private PowerManagerService mPowerManager;
- private NotificationManager mNotificationManager;
-
- private KeyguardManager.KeyguardLock mKeyguardLock;
- private boolean mKeyguardDisabled;
- private LockPatternUtils mLockPatternUtils;
-
- private AlarmManager mAlarmManager;
-
- private LocationManager mLocationManager;
- private Location mLocation;
- private StatusBarManager mStatusBarManager;
-
- // The broadcast receiver which receives the result of the ordered broadcast sent when
- // the dock state changes. The original ordered broadcast is sent with an initial result
- // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
- // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
- private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() != Activity.RESULT_OK) {
- return;
- }
-
- // Launch a dock activity
- String category;
- if (mCarModeEnabled) {
- // Only launch car home when car mode is enabled.
- category = Intent.CATEGORY_CAR_DOCK;
- } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
- category = Intent.CATEGORY_DESK_DOCK;
- } else {
- category = null;
- }
- if (category != null) {
- intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Slog.w(TAG, e.getCause());
- }
- }
- }
- };
-
- private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- };
-
- // A LocationListener to initialize the network location provider. The location updates
- // are handled through the passive location provider.
- private final LocationListener mEmptyLocationListener = new LocationListener() {
- public void onLocationChanged(Location location) {
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
-
- private final LocationListener mLocationListener = new LocationListener() {
-
- public void onLocationChanged(Location location) {
- final boolean hasMoved = hasMoved(location);
- if (hasMoved || hasBetterAccuracy(location)) {
- synchronized (this) {
- mLocation = location;
- }
- if (hasMoved && mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
-
- private boolean hasBetterAccuracy(Location location) {
- if (location == null) {
- return false;
- }
- if (mLocation == null) {
- return true;
- }
- return location.getAccuracy() < mLocation.getAccuracy();
- }
-
- /*
- * The user has moved if the accuracy circles of the two locations
- * don't overlap.
- */
- private boolean hasMoved(Location location) {
- if (location == null) {
- return false;
- }
- if (mLocation == null) {
- return true;
- }
-
- /* if new location is older than the current one, the devices hasn't
- * moved.
- */
- if (location.getTime() < mLocation.getTime()) {
- return false;
- }
-
- /* Get the distance between the two points */
- float distance = mLocation.distanceTo(location);
-
- /* Get the total accuracy radius for both locations */
- float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
-
- /* If the distance is greater than the combined accuracy of the two
- * points then they can't overlap and hence the user has moved.
- */
- return distance >= totalAccuracy;
- }
- };
public DockObserver(Context context, PowerManagerService pm) {
mContext = context;
mPowerManager = pm;
- mLockPatternUtils = new LockPatternUtils(context);
init(); // set initial status
- ServiceManager.addService("uimode", mBinder);
-
- mAlarmManager =
- (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
- mLocationManager =
- (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
- mContext.registerReceiver(mTwilightUpdateReceiver,
- new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
-
startObserving(DOCK_UEVENT_MATCH);
}
@@ -272,14 +78,6 @@
if (newState != mDockState) {
mPreviousDockState = mDockState;
mDockState = newState;
- boolean carModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
- if (mCarModeEnabled != carModeEnabled) {
- try {
- setCarMode(carModeEnabled);
- } catch (RemoteException e1) {
- Slog.w(TAG, "Unable to change car mode.", e1);
- }
- }
if (mSystemReady) {
// Don't force screen on when undocking from the desk dock.
// The change in power state will do this anyway.
@@ -315,24 +113,11 @@
void systemReady() {
synchronized (this) {
- KeyguardManager keyguardManager =
- (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mKeyguardLock = keyguardManager.newKeyguardLock(TAG);
-
- final boolean enableCarMode = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
- if (enableCarMode) {
- try {
- setCarMode(enableCarMode);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change car mode.", e);
- }
- }
// don't bother broadcasting undocked here
if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
update();
}
mSystemReady = true;
- mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
}
@@ -341,10 +126,6 @@
}
private final Handler mHandler = new Handler() {
-
- boolean mPassiveListenerEnabled;
- boolean mNetworkListenerEnabled;
-
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -362,17 +143,7 @@
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- if (mCarModeEnabled && mDockState != Intent.EXTRA_DOCK_STATE_CAR) {
- // Pretend to be in DOCK_STATE_CAR.
- intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR);
- } else if (!mCarModeEnabled && mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
- // Pretend to be in DOCK_STATE_UNDOCKED.
- intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
- } else {
- intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
- }
- intent.putExtra(Intent.EXTRA_PHYSICAL_DOCK_STATE, mDockState);
- intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
// Check if this is Bluetooth Dock
String address = BluetoothService.readDockBluetoothAddress();
@@ -415,296 +186,10 @@
}
}
- // Send the ordered broadcast; the result receiver will receive after all
- // broadcasts have been sent. If any broadcast receiver changes the result
- // code from the initial value of RESULT_OK, then the result receiver will
- // not launch the corresponding dock application. This gives apps a chance
- // to override the behavior and stay in their app even when the device is
- // placed into a dock.
- mContext.sendStickyOrderedBroadcast(
- intent, mResultReceiver, null, Activity.RESULT_OK, null, null);
-
- }
- break;
- case MSG_UPDATE_TWILIGHT:
- synchronized (this) {
- if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
- try {
- DockObserver.this.updateTwilight();
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change night mode.", e);
- }
- }
- }
- break;
- case MSG_ENABLE_LOCATION_UPDATES:
- // enable passive provider to receive updates from location fixes (gps
- // and network).
- boolean passiveLocationEnabled;
- try {
- passiveLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if passive location provider
- // does not exist or is not yet installed.
- passiveLocationEnabled = false;
- }
- if (!mPassiveListenerEnabled && passiveLocationEnabled) {
- mPassiveListenerEnabled = true;
- mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
- 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
- }
- // enable network provider to receive at least location updates for a given
- // distance.
- boolean networkLocationEnabled;
- try {
- networkLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if network location provider
- // does not exist or is not yet installed.
- networkLocationEnabled = false;
- }
- if (!mNetworkListenerEnabled && networkLocationEnabled) {
- mNetworkListenerEnabled = true;
- mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
- LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
-
- retrieveLocation();
- if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
- try {
- DockObserver.this.updateTwilight();
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change night mode.", e);
- }
- }
- }
- if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
- long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
- interval *= 1.5;
- if (interval == 0) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
- } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
- }
- Bundle bundle = new Bundle();
- bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
- Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
- newMsg.setData(bundle);
- mHandler.sendMessageDelayed(newMsg, interval);
+ mContext.sendStickyBroadcast(intent);
}
break;
}
}
-
- private void retrieveLocation() {
- final Location gpsLocation =
- mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
- Location location;
- Criteria criteria = new Criteria();
- criteria.setSpeedRequired(false);
- criteria.setAltitudeRequired(false);
- criteria.setBearingRequired(false);
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
- final String bestProvider = mLocationManager.getBestProvider(criteria, true);
- location = mLocationManager.getLastKnownLocation(bestProvider);
- // In the case there is no location available (e.g. GPS fix or network location
- // is not available yet), the longitude of the location is estimated using the timezone,
- // latitude and accuracy are set to get a good average.
- if (location == null) {
- Time currentTime = new Time();
- currentTime.set(System.currentTimeMillis());
- double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
- (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
- location = new Location("fake");
- location.setLongitude(lngOffset);
- location.setLatitude(0);
- location.setAccuracy(417000.0f);
- location.setTime(System.currentTimeMillis());
- }
- synchronized (this) {
- mLocation = location;
- }
- }
- };
-
- private void adjustStatusBarCarMode() {
- if (mStatusBarManager == null) {
- mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
- }
-
- // Fear not: StatusBarService manages a list of requests to disable
- // features of the status bar; these are ORed together to form the
- // active disabled list. So if (for example) the device is locked and
- // the status bar should be totally disabled, the calls below will
- // have no effect until the device is unlocked.
- if (mStatusBarManager != null) {
- long ident = Binder.clearCallingIdentity();
- mStatusBarManager.disable(mCarModeEnabled
- ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
- : StatusBarManager.DISABLE_NONE);
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- if (mNotificationManager != null) {
- long ident = Binder.clearCallingIdentity();
- if (mCarModeEnabled) {
- Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
-
- Notification n = new Notification();
- n.icon = R.drawable.stat_notify_car_mode;
- n.defaults = Notification.DEFAULT_LIGHTS;
- n.flags = Notification.FLAG_ONGOING_EVENT;
- n.when = 0;
- n.setLatestEventInfo(
- mContext,
- mContext.getString(R.string.car_mode_disable_notification_title),
- mContext.getString(R.string.car_mode_disable_notification_message),
- PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
- mNotificationManager.notify(0, n);
- } else {
- mNotificationManager.cancel(0);
- }
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void setCarMode(boolean enabled) throws RemoteException {
- mCarModeEnabled = enabled;
- if (enabled) {
- if (mNightMode == MODE_NIGHT_AUTO) {
- updateTwilight();
- } else {
- setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode << 4);
- }
- } else {
- // Disabling the car mode clears the night mode.
- setMode(Configuration.UI_MODE_TYPE_NORMAL,
- Configuration.UI_MODE_NIGHT_UNDEFINED);
- }
- adjustStatusBarCarMode();
- }
-
- private void setMode(int modeType, int modeNight) throws RemoteException {
- long ident = Binder.clearCallingIdentity();
- final IActivityManager am = ActivityManagerNative.getDefault();
- Configuration config = am.getConfiguration();
- if (config.uiMode != (modeType | modeNight)) {
- config.uiMode = modeType | modeNight;
- am.updateConfiguration(config);
- }
- Binder.restoreCallingIdentity(ident);
- }
-
- private void setNightMode(int mode) throws RemoteException {
- if (mNightMode != mode) {
- mNightMode = mode;
- switch (mode) {
- case MODE_NIGHT_NO:
- case MODE_NIGHT_YES:
- setMode(Configuration.UI_MODE_TYPE_CAR, mode << 4);
- break;
- case MODE_NIGHT_AUTO:
- long ident = Binder.clearCallingIdentity();
- updateTwilight();
- Binder.restoreCallingIdentity(ident);
- break;
- default:
- setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4);
- break;
- }
- }
- }
-
- private void updateTwilight() throws RemoteException {
- synchronized (this) {
- if (mLocation == null) {
- return;
- }
- final long currentTime = System.currentTimeMillis();
- int nightMode;
- // calculate current twilight
- TwilightCalculator tw = new TwilightCalculator();
- tw.calculateTwilight(currentTime,
- mLocation.getLatitude(), mLocation.getLongitude());
- if (tw.mState == TwilightCalculator.DAY) {
- nightMode = MODE_NIGHT_NO;
- } else {
- nightMode = MODE_NIGHT_YES;
- }
-
- // schedule next update
- long nextUpdate = 0;
- if (tw.mSunrise == -1 || tw.mSunset == -1) {
- // In the case the day or night never ends the update is scheduled 12 hours later.
- nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
- } else {
- final int mLastTwilightState = tw.mState;
- // add some extra time to be on the save side.
- nextUpdate += DateUtils.MINUTE_IN_MILLIS;
- if (currentTime > tw.mSunset) {
- // next update should be on the following day
- tw.calculateTwilight(currentTime
- + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
- mLocation.getLongitude());
- }
-
- if (mLastTwilightState == TwilightCalculator.NIGHT) {
- nextUpdate += tw.mSunrise;
- } else {
- nextUpdate += tw.mSunset;
- }
- }
-
- Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
- mAlarmManager.cancel(pendingIntent);
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
-
- // Make sure that we really set the new mode only if we're in car mode and
- // automatic switching is enables.
- if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
- }
- }
- }
-
- /**
- * Wrapper class implementing the IUiModeManager interface.
- */
- private final IUiModeManager.Stub mBinder = new IUiModeManager.Stub() {
-
- public void disableCarMode() throws RemoteException {
- if (mCarModeEnabled) {
- setCarMode(false);
- update();
- }
- }
-
- public void enableCarMode() throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ENABLE_CAR_MODE,
- "Need ENABLE_CAR_MODE permission");
- if (!mCarModeEnabled) {
- setCarMode(true);
- update();
- }
- }
-
- public void setNightMode(int mode) throws RemoteException {
- if (mCarModeEnabled) {
- DockObserver.this.setNightMode(mode);
- }
- }
-
- public int getNightMode() throws RemoteException {
- return mNightMode;
- }
};
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 9e11546..6ce2582 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -2884,17 +2884,19 @@
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
- String names[] = p.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- if (mProviders.containsKey(names[j])) {
- PackageParser.Provider other = mProviders.get(names[j]);
- Log.w(TAG, "Can't install because provider name " + names[j] +
- " (in package " + pkg.applicationInfo.packageName +
- ") is already used by "
- + ((other != null && other.getComponentName() != null)
- ? other.getComponentName().getPackageName() : "?"));
- mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
- return null;
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ if (mProviders.containsKey(names[j])) {
+ PackageParser.Provider other = mProviders.get(names[j]);
+ Log.w(TAG, "Can't install because provider name " + names[j] +
+ " (in package " + pkg.applicationInfo.packageName +
+ ") is already used by "
+ + ((other != null && other.getComponentName() != null)
+ ? other.getComponentName().getPackageName() : "?"));
+ mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
+ return null;
+ }
}
}
}
@@ -3103,38 +3105,40 @@
mProvidersByComponent.put(new ComponentName(p.info.packageName,
p.info.name), p);
p.syncable = p.info.isSyncable;
- String names[] = p.info.authority.split(";");
- p.info.authority = null;
- for (int j = 0; j < names.length; j++) {
- if (j == 1 && p.syncable) {
- // We only want the first authority for a provider to possibly be
- // syncable, so if we already added this provider using a different
- // authority clear the syncable flag. We copy the provider before
- // changing it because the mProviders object contains a reference
- // to a provider that we don't want to change.
- // Only do this for the second authority since the resulting provider
- // object can be the same for all future authorities for this provider.
- p = new PackageParser.Provider(p);
- p.syncable = false;
- }
- if (!mProviders.containsKey(names[j])) {
- mProviders.put(names[j], p);
- if (p.info.authority == null) {
- p.info.authority = names[j];
- } else {
- p.info.authority = p.info.authority + ";" + names[j];
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
+ p.info.authority = null;
+ for (int j = 0; j < names.length; j++) {
+ if (j == 1 && p.syncable) {
+ // We only want the first authority for a provider to possibly be
+ // syncable, so if we already added this provider using a different
+ // authority clear the syncable flag. We copy the provider before
+ // changing it because the mProviders object contains a reference
+ // to a provider that we don't want to change.
+ // Only do this for the second authority since the resulting provider
+ // object can be the same for all future authorities for this provider.
+ p = new PackageParser.Provider(p);
+ p.syncable = false;
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
- Log.d(TAG, "Registered content provider: " + names[j] +
- ", className = " + p.info.name +
- ", isSyncable = " + p.info.isSyncable);
- } else {
- PackageParser.Provider other = mProviders.get(names[j]);
- Log.w(TAG, "Skipping provider name " + names[j] +
- " (in package " + pkg.applicationInfo.packageName +
- "): name already used by "
- + ((other != null && other.getComponentName() != null)
- ? other.getComponentName().getPackageName() : "?"));
+ if (!mProviders.containsKey(names[j])) {
+ mProviders.put(names[j], p);
+ if (p.info.authority == null) {
+ p.info.authority = names[j];
+ } else {
+ p.info.authority = p.info.authority + ";" + names[j];
+ }
+ if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
+ Log.d(TAG, "Registered content provider: " + names[j] +
+ ", className = " + p.info.name +
+ ", isSyncable = " + p.info.isSyncable);
+ } else {
+ PackageParser.Provider other = mProviders.get(names[j]);
+ Log.w(TAG, "Skipping provider name " + names[j] +
+ " (in package " + pkg.applicationInfo.packageName +
+ "): name already used by "
+ + ((other != null && other.getComponentName() != null)
+ ? other.getComponentName().getPackageName() : "?"));
+ }
}
}
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index deee7f3..b023958 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
DockObserver dock = null;
+ UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
// Critical services...
@@ -363,6 +364,14 @@
}
try {
+ Slog.i(TAG, "UI Mode Manager Service");
+ // Listen for dock station changes
+ uiMode = new UiModeManagerService(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting UiModeManagerService", e);
+ }
+
+ try {
Slog.i(TAG, "Backup Service");
ServiceManager.addService(Context.BACKUP_SERVICE,
new BackupManagerService(context));
@@ -441,6 +450,7 @@
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
+ final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
@@ -460,6 +470,7 @@
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
+ if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
new file mode 100644
index 0000000..71826ff
--- /dev/null
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.KeyguardManager;
+import android.app.IUiModeManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.app.UiModeManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.R;
+import com.android.internal.app.DisableCarModeActivity;
+
+class UiModeManagerService extends IUiModeManager.Stub {
+ private static final String TAG = UiModeManager.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
+
+ private static final int MSG_UPDATE_TWILIGHT = 0;
+ private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+
+ private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+ private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
+ private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+
+ private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
+
+ private final Context mContext;
+
+ final Object mLock = new Object();
+
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+ private boolean mCarModeEnabled = false;
+ private boolean mCharging = false;
+ private final boolean mCarModeKeepsScreenOn;
+ private final boolean mDeskModeKeepsScreenOn;
+
+ private boolean mComputedNightMode;
+ private int mCurUiMode = 0;
+
+ private Configuration mConfiguration = new Configuration();
+
+ private boolean mSystemReady;
+
+ private NotificationManager mNotificationManager;
+
+ private AlarmManager mAlarmManager;
+
+ private LocationManager mLocationManager;
+ private Location mLocation;
+ private StatusBarManager mStatusBarManager;
+ private KeyguardManager.KeyguardLock mKeyguardLock;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // The broadcast receiver which receives the result of the ordered broadcast sent when
+ // the dock state changes. The original ordered broadcast is sent with an initial result
+ // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
+ // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
+ private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() != Activity.RESULT_OK) {
+ return;
+ }
+
+ // Launch a dock activity
+ String category;
+ if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+ // Only launch car home when car mode is enabled.
+ category = Intent.CATEGORY_CAR_DOCK;
+ } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
+ category = Intent.CATEGORY_DESK_DOCK;
+ } else {
+ category = null;
+ }
+ if (category != null) {
+ intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Slog.w(TAG, e.getCause());
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+ }
+ }
+ };
+
+ private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ updateDockState(state);
+ }
+ };
+
+ private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+ synchronized (mLock) {
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+ };
+
+ // A LocationListener to initialize the network location provider. The location updates
+ // are handled through the passive location provider.
+ private final LocationListener mEmptyLocationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+
+ private final LocationListener mLocationListener = new LocationListener() {
+
+ public void onLocationChanged(Location location) {
+ final boolean hasMoved = hasMoved(location);
+ final boolean hasBetterAccuracy = mLocation == null
+ || location.getAccuracy() < mLocation.getAccuracy();
+ if (hasMoved || hasBetterAccuracy) {
+ synchronized (mLock) {
+ mLocation = location;
+ if (hasMoved && isDoingNightMode()
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+ }
+ }
+ }
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ /*
+ * The user has moved if the accuracy circles of the two locations
+ * don't overlap.
+ */
+ private boolean hasMoved(Location location) {
+ if (location == null) {
+ return false;
+ }
+ if (mLocation == null) {
+ return true;
+ }
+
+ /* if new location is older than the current one, the devices hasn't
+ * moved.
+ */
+ if (location.getTime() < mLocation.getTime()) {
+ return false;
+ }
+
+ /* Get the distance between the two points */
+ float distance = mLocation.distanceTo(location);
+
+ /* Get the total accuracy radius for both locations */
+ float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
+
+ /* If the distance is greater than the combined accuracy of the two
+ * points then they can't overlap and hence the user has moved.
+ */
+ return distance >= totalAccuracy;
+ }
+ };
+
+ public UiModeManagerService(Context context) {
+ mContext = context;
+
+ ServiceManager.addService(Context.UI_MODE_SERVICE, this);
+
+ mAlarmManager =
+ (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mLocationManager =
+ (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
+ mContext.registerReceiver(mTwilightUpdateReceiver,
+ new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
+ mContext.registerReceiver(mDockModeReceiver,
+ new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ mContext.registerReceiver(mBatteryReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+ PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+
+ mConfiguration.setToDefaults();
+
+ mCarModeKeepsScreenOn = (context.getResources().getInteger(
+ com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
+ mDeskModeKeepsScreenOn = (context.getResources().getInteger(
+ com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
+ }
+
+ public void disableCarMode() {
+ synchronized (mLock) {
+ setCarModeLocked(false);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+
+ public void enableCarMode() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ENABLE_CAR_MODE,
+ "Need ENABLE_CAR_MODE permission");
+ synchronized (mLock) {
+ setCarModeLocked(true);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+
+ public int getCurrentModeType() {
+ synchronized (mLock) {
+ return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
+ }
+ }
+
+ public void setNightMode(int mode) throws RemoteException {
+ synchronized (mLock) {
+ switch (mode) {
+ case UiModeManager.MODE_NIGHT_NO:
+ case UiModeManager.MODE_NIGHT_YES:
+ case UiModeManager.MODE_NIGHT_AUTO:
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown mode: " + mode);
+ }
+ if (!isDoingNightMode()) {
+ return;
+ }
+
+ if (mNightMode != mode) {
+ mNightMode = mode;
+ updateLocked();
+ }
+ }
+ }
+
+ public int getNightMode() throws RemoteException {
+ return mNightMode;
+ }
+
+ void systemReady() {
+ synchronized (mLock) {
+ mSystemReady = true;
+ mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+ updateLocked();
+ mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
+ }
+ }
+
+ boolean isDoingNightMode() {
+ return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ }
+
+ void setCarModeLocked(boolean enabled) {
+ if (mCarModeEnabled != enabled) {
+ mCarModeEnabled = enabled;
+
+ // Disable keyguard when in car mode
+ if (mKeyguardLock == null) {
+ KeyguardManager km =
+ (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (km != null) {
+ mKeyguardLock = km.newKeyguardLock(TAG);
+ }
+ }
+ if (mKeyguardLock != null) {
+ if (enabled) {
+ mKeyguardLock.disableKeyguard();
+ } else {
+ mKeyguardLock.reenableKeyguard();
+ }
+ }
+ }
+ }
+
+ void updateDockState(int newState) {
+ synchronized (mLock) {
+ if (newState != mDockState) {
+ mDockState = newState;
+ setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+ }
+
+ final void updateLocked() {
+ long ident = Binder.clearCallingIdentity();
+
+ try {
+ int uiMode = 0;
+ if (mCarModeEnabled) {
+ uiMode = Configuration.UI_MODE_TYPE_CAR;
+ } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ uiMode = Configuration.UI_MODE_TYPE_DESK;
+ }
+ if (uiMode != 0) {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
+ : Configuration.UI_MODE_NIGHT_NO;
+ } else {
+ uiMode |= mNightMode << 4;
+ }
+ } else {
+ // Disabling the car mode clears the night mode.
+ uiMode = Configuration.UI_MODE_TYPE_NORMAL |
+ Configuration.UI_MODE_NIGHT_NO;
+ }
+
+ if (uiMode != mCurUiMode) {
+ mCurUiMode = uiMode;
+
+ try {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ mConfiguration.uiMode = uiMode;
+ am.updateConfiguration(mConfiguration);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure communicating with activity manager", e);
+ }
+ }
+
+ String action = null;
+ String oldAction = null;
+ if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+ oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
+ } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
+ oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
+ }
+
+ if (mCarModeEnabled) {
+ if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
+ adjustStatusBarCarModeLocked();
+
+ if (oldAction != null) {
+ mContext.sendBroadcast(new Intent(oldAction));
+ }
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
+ action = UiModeManager.ACTION_ENTER_CAR_MODE;
+ }
+ } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) {
+ if (oldAction != null) {
+ mContext.sendBroadcast(new Intent(oldAction));
+ }
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK;
+ action = UiModeManager.ACTION_ENTER_DESK_MODE;
+ }
+ } else {
+ if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+ adjustStatusBarCarModeLocked();
+ }
+
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ action = oldAction;
+ }
+
+ if (action != null) {
+ // Send the ordered broadcast; the result receiver will receive after all
+ // broadcasts have been sent. If any broadcast receiver changes the result
+ // code from the initial value of RESULT_OK, then the result receiver will
+ // not launch the corresponding dock application. This gives apps a chance
+ // to override the behavior and stay in their app even when the device is
+ // placed into a dock.
+ mContext.sendOrderedBroadcast(new Intent(action), null,
+ mResultReceiver, null, Activity.RESULT_OK, null, null);
+ }
+
+ // keep screen on when charging and in car mode
+ boolean keepScreenOn = mCharging &&
+ ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
+ (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
+ if (keepScreenOn != mWakeLock.isHeld()) {
+ if (keepScreenOn) {
+ mWakeLock.acquire();
+ } else {
+ mWakeLock.release();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void adjustStatusBarCarModeLocked() {
+ if (mStatusBarManager == null) {
+ mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+ }
+
+ // Fear not: StatusBarService manages a list of requests to disable
+ // features of the status bar; these are ORed together to form the
+ // active disabled list. So if (for example) the device is locked and
+ // the status bar should be totally disabled, the calls below will
+ // have no effect until the device is unlocked.
+ if (mStatusBarManager != null) {
+ mStatusBarManager.disable(mCarModeEnabled
+ ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
+ : StatusBarManager.DISABLE_NONE);
+ }
+
+ if (mNotificationManager == null) {
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ if (mNotificationManager != null) {
+ if (mCarModeEnabled) {
+ Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
+
+ Notification n = new Notification();
+ n.icon = R.drawable.stat_notify_car_mode;
+ n.defaults = Notification.DEFAULT_LIGHTS;
+ n.flags = Notification.FLAG_ONGOING_EVENT;
+ n.when = 0;
+ n.setLatestEventInfo(
+ mContext,
+ mContext.getString(R.string.car_mode_disable_notification_title),
+ mContext.getString(R.string.car_mode_disable_notification_message),
+ PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
+ mNotificationManager.notify(0, n);
+ } else {
+ mNotificationManager.cancel(0);
+ }
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+
+ boolean mPassiveListenerEnabled;
+ boolean mNetworkListenerEnabled;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TWILIGHT:
+ synchronized (mLock) {
+ if (isDoingNightMode() && mLocation != null
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ updateLocked();
+ }
+ }
+ break;
+ case MSG_ENABLE_LOCATION_UPDATES:
+ // enable network provider to receive at least location updates for a given
+ // distance.
+ boolean networkLocationEnabled;
+ try {
+ networkLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if network location provider
+ // does not exist or is not yet installed.
+ networkLocationEnabled = false;
+ }
+ if (!mNetworkListenerEnabled && networkLocationEnabled) {
+ mNetworkListenerEnabled = true;
+ mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+ LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
+
+ if (mLocation == null) {
+ retrieveLocation();
+ }
+ synchronized (mLock) {
+ if (isDoingNightMode() && mLocation != null
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ updateLocked();
+ }
+ }
+ }
+ // enable passive provider to receive updates from location fixes (gps
+ // and network).
+ boolean passiveLocationEnabled;
+ try {
+ passiveLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if passive location provider
+ // does not exist or is not yet installed.
+ passiveLocationEnabled = false;
+ }
+ if (!mPassiveListenerEnabled && passiveLocationEnabled) {
+ mPassiveListenerEnabled = true;
+ mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
+ }
+ if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
+ long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
+ interval *= 1.5;
+ if (interval == 0) {
+ interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
+ } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
+ interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
+ Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
+ newMsg.setData(bundle);
+ mHandler.sendMessageDelayed(newMsg, interval);
+ }
+ break;
+ }
+ }
+
+ private void retrieveLocation() {
+ Location location;
+ Criteria criteria = new Criteria();
+ criteria.setSpeedRequired(false);
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingRequired(false);
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
+ final String bestProvider = mLocationManager.getBestProvider(criteria, true);
+ location = mLocationManager.getLastKnownLocation(bestProvider);
+ // In the case there is no location available (e.g. GPS fix or network location
+ // is not available yet), the longitude of the location is estimated using the timezone,
+ // latitude and accuracy are set to get a good average.
+ if (location == null) {
+ Time currentTime = new Time();
+ currentTime.set(System.currentTimeMillis());
+ double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+ (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
+ location = new Location("fake");
+ location.setLongitude(lngOffset);
+ location.setLatitude(0);
+ location.setAccuracy(417000.0f);
+ location.setTime(System.currentTimeMillis());
+ }
+ synchronized (mLock) {
+ mLocation = location;
+ }
+ }
+ };
+
+ void updateTwilightLocked() {
+ if (mLocation == null) {
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ boolean nightMode;
+ // calculate current twilight
+ TwilightCalculator tw = new TwilightCalculator();
+ tw.calculateTwilight(currentTime,
+ mLocation.getLatitude(), mLocation.getLongitude());
+ if (tw.mState == TwilightCalculator.DAY) {
+ nightMode = false;
+ } else {
+ nightMode = true;
+ }
+
+ // schedule next update
+ long nextUpdate = 0;
+ if (tw.mSunrise == -1 || tw.mSunset == -1) {
+ // In the case the day or night never ends the update is scheduled 12 hours later.
+ nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
+ } else {
+ final int mLastTwilightState = tw.mState;
+ // add some extra time to be on the save side.
+ nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+ if (currentTime > tw.mSunset) {
+ // next update should be on the following day
+ tw.calculateTwilight(currentTime
+ + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
+ mLocation.getLongitude());
+ }
+
+ if (mLastTwilightState == TwilightCalculator.NIGHT) {
+ nextUpdate += tw.mSunrise;
+ } else {
+ nextUpdate += tw.mSunset;
+ }
+ }
+
+ Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
+ mAlarmManager.cancel(pendingIntent);
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+
+ mComputedNightMode = nightMode;
+ }
+
+ @Override
+ protected 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 uimode service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("Current UI Mode Service state:");
+ pw.print(" mDockState="); pw.print(mDockState);
+ pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+ pw.print(" mNightMode="); pw.print(mNightMode);
+ pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
+ pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
+ pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
+ pw.print(" mSystemReady="); pw.println(mSystemReady);
+ if (mLocation != null) {
+ pw.print(" mLocation="); pw.println(mLocation);
+ }
+ }
+ }
+}
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 4078622..3fd71c8 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -32,6 +32,7 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -113,6 +114,15 @@
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
+ /**
+ * @hide
+ */
+ @SuppressWarnings("unused")
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ return MockContentProvider.this.call(method, request, args);
+ }
+
public IBinder asBinder() {
throw new UnsupportedOperationException();
}
@@ -205,6 +215,14 @@
}
/**
+ * @hide
+ */
+ @Override
+ public Bundle call(String method, String request, Bundle args) {
+ throw new UnsupportedOperationException("unimplemented mock method call");
+ }
+
+ /**
* Returns IContentProvider which calls back same methods in this class.
* By overriding this class, we avoid the mechanism hidden behind ContentProvider
* (IPC, etc.)
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 7c0a1e2..0be5bea 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -27,6 +27,7 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -38,7 +39,7 @@
* {@link java.lang.UnsupportedOperationException}. Tests can extend this class to
* implement behavior needed for tests.
*
- * @hide - @hide because this exposes bulkQuery(), which must also be hidden.
+ * @hide - @hide because this exposes bulkQuery() and call(), which must also be hidden.
*/
public class MockIContentProvider implements IContentProvider {
public int bulkInsert(Uri url, ContentValues[] initialValues) {
@@ -93,6 +94,11 @@
throw new UnsupportedOperationException("unimplemented mock method");
}
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
public IBinder asBinder() {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6e7a66d..fc655a7 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -820,7 +820,12 @@
if (strcmp(name, kWildcardName) == 0) {
if (out) out->uiMode =
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_NORMAL;
+ | ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_DESK;
return true;
} else if (strcmp(name, "car") == 0) {
if (out) out->uiMode =
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ea021d8..b7580b3 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -447,7 +447,7 @@
static bool applyFileOverlay(Bundle *bundle,
const sp<AaptAssets>& assets,
- const sp<ResourceTypeSet>& baseSet,
+ sp<ResourceTypeSet> *baseSet,
const char *resType)
{
if (bundle->getVerbose()) {
@@ -475,13 +475,16 @@
if (bundle->getVerbose()) {
printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
}
- size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
+ size_t baseIndex = UNKNOWN_ERROR;
+ if (baseSet->get() != NULL) {
+ baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
+ }
if (baseIndex < UNKNOWN_ERROR) {
// look for same flavor. For a given file (strings.xml, for example)
// there may be a locale specific or other flavors - we want to match
// the same flavor.
sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
- sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
+ sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
overlayGroup->getFiles();
@@ -520,8 +523,12 @@
assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
}
} else {
+ if (baseSet->get() == NULL) {
+ *baseSet = new ResourceTypeSet();
+ assets->getResources()->add(String8(resType), *baseSet);
+ }
// this group doesn't exist (a file that's only in the overlay)
- baseSet->add(overlaySet->keyAt(overlayIndex),
+ (*baseSet)->add(overlaySet->keyAt(overlayIndex),
overlaySet->valueAt(overlayIndex));
// make sure all flavors are defined in the resources.
sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
@@ -751,13 +758,13 @@
current = current->getOverlay();
}
// apply the overlay files to the base set
- if (!applyFileOverlay(bundle, assets, drawables, "drawable") ||
- !applyFileOverlay(bundle, assets, layouts, "layout") ||
- !applyFileOverlay(bundle, assets, anims, "anim") ||
- !applyFileOverlay(bundle, assets, xmls, "xml") ||
- !applyFileOverlay(bundle, assets, raws, "raw") ||
- !applyFileOverlay(bundle, assets, colors, "color") ||
- !applyFileOverlay(bundle, assets, menus, "menu")) {
+ if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
+ !applyFileOverlay(bundle, assets, &layouts, "layout") ||
+ !applyFileOverlay(bundle, assets, &anims, "anim") ||
+ !applyFileOverlay(bundle, assets, &xmls, "xml") ||
+ !applyFileOverlay(bundle, assets, &raws, "raw") ||
+ !applyFileOverlay(bundle, assets, &colors, "color") ||
+ !applyFileOverlay(bundle, assets, &menus, "menu")) {
return UNKNOWN_ERROR;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0b531c2..1f9d152 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2369,7 +2369,7 @@
if (configSet.find(region) == configSet.end()) {
if (configSet.count(defaultLocale) == 0) {
fprintf(stdout, "aapt: warning: "
- "*** string '%s' has no default or required localization "
+ "**** string '%s' has no default or required localization "
"for '%s' in %s\n",
String8(nameIter->first).string(),
config.string(),