Merge "Fix 3148496: Update LockScreen clock to use new font."
diff --git a/api/current.xml b/api/current.xml
index 0cbfd76..f915fad 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3265,6 +3265,17 @@
visibility="public"
>
</field>
+<field name="customTokens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843593"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="cycles"
type="int"
transient="false"
@@ -18308,6 +18319,28 @@
visibility="public"
>
</field>
+<field name="KEY_CALLER_PID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""callerPid""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEY_CALLER_UID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""callerUid""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="KEY_ERROR_CODE"
type="java.lang.String"
transient="false"
@@ -18555,6 +18588,28 @@
</parameter>
<parameter name="prefId" type="int">
</parameter>
+<parameter name="customTokens" type="boolean">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorDescription"
+ type="android.accounts.AuthenticatorDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="labelId" type="int">
+</parameter>
+<parameter name="iconId" type="int">
+</parameter>
+<parameter name="smallIconId" type="int">
+</parameter>
+<parameter name="prefId" type="int">
+</parameter>
</constructor>
<method name="describeContents"
return="int"
@@ -18615,6 +18670,16 @@
visibility="public"
>
</field>
+<field name="customTokens"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="iconId"
type="int"
transient="false"
@@ -32206,6 +32271,19 @@
visibility="public"
>
</constructor>
+<method name="destroyLoader"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
<method name="dump"
return="void"
abstract="true"
@@ -32274,12 +32352,12 @@
</method>
<method name="stopLoader"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="id" type="int">
@@ -32323,123 +32401,20 @@
<parameter name="data" type="D">
</parameter>
</method>
+<method name="onLoaderReset"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader<D>">
+</parameter>
+</method>
</interface>
-<class name="LoaderManagingFragment"
- extends="android.app.Fragment"
- abstract="true"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<implements name="android.content.Loader.OnLoadCompleteListener">
-</implements>
-<constructor name="LoaderManagingFragment"
- type="android.app.LoaderManagingFragment"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getLoader"
- return="android.content.Loader<D>"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-<method name="onCreateLoader"
- return="android.content.Loader<D>"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="onInitializeLoaders"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="onLoadComplete"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="loader" type="android.content.Loader<D>">
-</parameter>
-<parameter name="data" type="D">
-</parameter>
-</method>
-<method name="onLoadFinished"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="loader" type="android.content.Loader<D>">
-</parameter>
-<parameter name="data" type="D">
-</parameter>
-</method>
-<method name="startLoading"
- return="android.content.Loader<D>"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="stopLoading"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-</class>
<class name="LocalActivityManager"
extends="java.lang.Object"
abstract="false"
@@ -49433,17 +49408,6 @@
<parameter name="cursor" type="android.database.Cursor">
</parameter>
</method>
-<method name="destroy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getProjection"
return="java.lang.String[]"
abstract="false"
@@ -54750,12 +54714,12 @@
</method>
<method name="destroy"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -54818,6 +54782,17 @@
<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener<D>">
</parameter>
</method>
+<method name="reset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="startLoading"
return="void"
abstract="true"
@@ -149633,6 +149608,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_SKIP_UI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.intent.extra.alarm.SKIP_UI""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<interface name="BaseColumns"
abstract="true"
@@ -166218,6 +166204,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_WEB_SEARCH_ONLY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.speech.extra.WEB_SEARCH_ONLY""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="LANGUAGE_MODEL_FREE_FORM"
type="java.lang.String"
transient="false"
@@ -223299,7 +223296,7 @@
abstract="false"
static="false"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="CacheManager"
@@ -223317,7 +223314,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -223339,7 +223336,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -223354,7 +223351,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -223365,7 +223362,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -223390,7 +223387,7 @@
abstract="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="CacheManager.CacheResult"
@@ -234061,7 +234058,7 @@
type="android.widget.CursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -234234,7 +234231,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="protected"
>
<parameter name="context" type="android.content.Context">
@@ -234244,23 +234241,6 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</method>
-<method name="init"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="c" type="android.database.Cursor">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
<method name="newDropDownView"
return="android.view.View"
abstract="false"
@@ -234332,6 +234312,19 @@
<parameter name="filterQueryProvider" type="android.widget.FilterQueryProvider">
</parameter>
</method>
+<method name="swapCursor"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newCursor" type="android.database.Cursor">
+</parameter>
+</method>
<field name="FLAG_AUTO_REQUERY"
type="int"
transient="false"
@@ -234339,7 +234332,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -243850,7 +243843,7 @@
type="android.widget.ResourceCursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -245282,6 +245275,24 @@
type="android.widget.SimpleCursorAdapter"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="from" type="java.lang.String[]">
+</parameter>
+<parameter name="to" type="int[]">
+</parameter>
+</constructor>
+<constructor name="SimpleCursorAdapter"
+ type="android.widget.SimpleCursorAdapter"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -245295,6 +245306,8 @@
</parameter>
<parameter name="to" type="int[]">
</parameter>
+<parameter name="flags" type="int">
+</parameter>
</constructor>
<method name="bindView"
return="void"
@@ -335145,7 +335158,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335213,7 +335226,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335237,7 +335250,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340880,7 +340893,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340891,7 +340904,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340902,7 +340915,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -343272,7 +343285,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -358346,7 +358359,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index 524d3f4..7214c50 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -38,7 +38,7 @@
* @hide
*/
/* package private */ class AccountAuthenticatorCache
- extends RegisteredServicesCache<AuthenticatorDescription>
+ extends RegisteredServicesCache<AuthenticatorDescription>
implements IAccountAuthenticatorCache {
private static final String TAG = "Account";
private static final MySerializer sSerializer = new MySerializer();
@@ -64,11 +64,13 @@
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
final int prefId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
+ final boolean customTokens = sa.getBoolean(
+ com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);
if (TextUtils.isEmpty(accountType)) {
return null;
}
- return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
- smallIconId, prefId);
+ return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
+ smallIconId, prefId, customTokens);
} finally {
sa.recycle();
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index fd3a0d0..6388dc5 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -188,6 +188,12 @@
public static final String KEY_ERROR_CODE = "errorCode";
public static final String KEY_ERROR_MESSAGE = "errorMessage";
public static final String KEY_USERDATA = "userdata";
+ /**
+ * Authenticators using 'customTokens' option will also get the UID of the
+ * caller
+ */
+ public static final String KEY_CALLER_UID = "callerUid";
+ public static final String KEY_CALLER_PID = "callerPid";
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index a815b3a..72da2ce 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -82,8 +82,6 @@
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
- private static final String NO_BROADCAST_FLAG = "nobroadcast";
-
private static final String TAG = "AccountManagerService";
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
@@ -375,14 +373,6 @@
if (account == null) {
return false;
}
- final boolean noBroadcast = account.type.equals(GOOGLE_ACCOUNT_TYPE)
- && extras != null && extras.getBoolean(NO_BROADCAST_FLAG, false);
- // Remove the 'nobroadcast' flag since we don't want it to persist in the db. It is instead
- // used as a control signal to indicate whether or not this insertion should result in
- // an accounts changed broadcast being sent.
- if (extras != null) {
- extras.remove(NO_BROADCAST_FLAG);
- }
db.beginTransaction();
try {
long numMatches = DatabaseUtils.longForQuery(db,
@@ -419,9 +409,7 @@
} finally {
db.endTransaction();
}
- if (!noBroadcast) {
- sendAccountsChangedBroadcast();
- }
+ sendAccountsChangedBroadcast();
return true;
}
@@ -776,10 +764,6 @@
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
- if (account.type.equals(GOOGLE_ACCOUNT_TYPE) && key.equals("broadcast")) {
- sendAccountsChangedBroadcast();
- return;
- }
try {
writeUserdataIntoDatabase(account, key, value);
} finally {
@@ -893,13 +877,29 @@
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final int callerUid = Binder.getCallingUid();
- final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+ final int callerPid = Binder.getCallingPid();
+
+ AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(account.type));
+ final boolean customTokens =
+ authenticatorInfo != null && authenticatorInfo.type.customTokens;
+
+ // skip the check if customTokens
+ final boolean permissionGranted = customTokens ||
+ permissionIsGranted(account, authTokenType, callerUid);
+
+ if (customTokens) {
+ // let authenticator know the identity of the caller
+ loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
+ loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
+ }
long identityToken = clearCallingIdentity();
try {
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
- if (permissionGranted) {
+ if (!customTokens && permissionGranted) {
String authToken = readAuthTokenFromCache(account, authTokenType);
if (authToken != null) {
Bundle result = new Bundle();
@@ -953,8 +953,10 @@
"the type and name should not be empty");
return;
}
- saveAuthTokenToDatabase(new Account(name, type),
- authTokenType, authToken);
+ if (!customTokens) {
+ saveAuthTokenToDatabase(new Account(name, type),
+ authTokenType, authToken);
+ }
}
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index c6515672..5d9abb0 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -44,9 +44,12 @@
/** The package name that can be used to lookup the resources from above. */
final public String packageName;
+ /** Authenticator handles its own token caching and permission screen */
+ final public boolean customTokens;
+
/** A constructor for a full AuthenticatorDescription */
public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
- int smallIconId, int prefId) {
+ int smallIconId, int prefId, boolean customTokens) {
if (type == null) throw new IllegalArgumentException("type cannot be null");
if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
this.type = type;
@@ -55,6 +58,12 @@
this.iconId = iconId;
this.smallIconId = smallIconId;
this.accountPreferencesId = prefId;
+ this.customTokens = customTokens;
+ }
+
+ public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
+ int smallIconId, int prefId) {
+ this(type, packageName, labelId, iconId, smallIconId, prefId, false);
}
/**
@@ -74,6 +83,7 @@
this.iconId = 0;
this.smallIconId = 0;
this.accountPreferencesId = 0;
+ this.customTokens = false;
}
private AuthenticatorDescription(Parcel source) {
@@ -83,6 +93,7 @@
this.iconId = source.readInt();
this.smallIconId = source.readInt();
this.accountPreferencesId = source.readInt();
+ this.customTokens = source.readByte() == 1;
}
/** @inheritDoc */
@@ -115,6 +126,7 @@
dest.writeInt(iconId);
dest.writeInt(smallIconId);
dest.writeInt(accountPreferencesId);
+ dest.writeByte((byte) (customTokens ? 1 : 0));
}
/** Used to create the object from a parcel. */
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index cb06c89..636f547 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -153,8 +153,9 @@
* we cache all of the current animations in this map for possible cancellation on
* another layout event.
*/
- private HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>();
- private HashMap<View, Animator> currentVisibilityAnimations = new HashMap<View, Animator>();
+ private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>();
+ private final HashMap<View, Animator> currentVisibilityAnimations =
+ new HashMap<View, Animator>();
/**
* This hashmap is used to track the listeners that have been added to the children of
@@ -165,7 +166,7 @@
* the process of setting up and running all appropriate animations is done, we need to
* remove these listeners and clear out the map.
*/
- private HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
+ private final HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
new HashMap<View, View.OnLayoutChangeListener>();
/**
@@ -599,12 +600,14 @@
// Remove the animation from the cache when it ends
anim.addListener(new AnimatorListenerAdapter() {
private boolean canceled = false;
+ @Override
public void onAnimationCancel(Animator animator) {
// we remove canceled animations immediately, not here
canceled = true;
child.removeOnLayoutChangeListener(listener);
layoutChangeListenerMap.remove(child);
}
+ @Override
public void onAnimationEnd(Animator animator) {
if (!canceled) {
currentChangingAnimations.remove(child);
@@ -662,7 +665,8 @@
}
if (mListeners != null) {
anim.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
currentVisibilityAnimations.remove(child);
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 50082f9..cfecec1 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -22,6 +22,7 @@
import android.util.AndroidRuntimeException;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
+import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.HashMap;
@@ -478,12 +479,17 @@
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
- * @param duration The length of the animation, in milliseconds.
+ * @param duration The length of the animation, in milliseconds. This value cannot
+ * be negative.
* @return ValueAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
*/
public ValueAnimator setDuration(long duration) {
+ if (duration < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ duration);
+ }
mDuration = duration;
return this;
}
@@ -829,12 +835,15 @@
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
- * @param value the interpolator to be used by this animation
+ * @param value the interpolator to be used by this animation. A value of <code>null</code>
+ * will result in linear interpolation.
*/
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
+ } else {
+ mInterpolator = new LinearInterpolator();
}
}
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 5dc29a9..d9cafb6 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -24,7 +24,6 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Message;
-import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index efe527f..448b3ef 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -71,10 +71,7 @@
int year,
int monthOfYear,
int dayOfMonth) {
- this(context, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
- ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
- : com.android.internal.R.style.Theme_Dialog_Alert,
- callBack, year, monthOfYear, dayOfMonth);
+ this(context, 0, callBack, year, monthOfYear, dayOfMonth);
}
/**
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 0ab987a..7125054 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -26,7 +26,19 @@
/**
* Interface associated with an {@link Activity} or {@link Fragment} for managing
- * one or more {@link android.content.Loader} instances associated with it.
+ * one or more {@link android.content.Loader} instances associated with it. This
+ * helps an application manage longer-running operations in conjunction with the
+ * Activity or Fragment lifecycle; the most common use of this is with a
+ * {@link android.content.CursorLoader}, however applications are free to write
+ * their own loaders for loading other types of data.
+ *
+ * <p>As an example, here is the full implementation of a {@link Fragment}
+ * that displays a {@link android.widget.ListView} containing the results of
+ * a query against the contacts content provider. It uses a
+ * {@link android.content.CursorLoader} to manage the query on the provider.
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
+ * fragment_cursor}
*/
public abstract class LoaderManager {
/**
@@ -49,10 +61,48 @@
* activity's state is saved. See {@link FragmentManager#openTransaction()
* FragmentManager.openTransaction()} for further discussion on this.
*
+ * <p>This function is guaranteed to be called prior to the release of
+ * the last data that was supplied for this Loader. At this point
+ * you should remove all use of the old data (since it will be released
+ * soon), but should not do your own release of the data since its Loader
+ * owns it and will take care of that. The Loader will take care of
+ * management of its data so you don't have to. In particular:
+ *
+ * <ul>
+ * <li> <p>The Loader will monitor for changes to the data, and report
+ * them to you through new calls here. You should not monitor the
+ * data yourself. For example, if the data is a {@link android.database.Cursor}
+ * and you place it in a {@link android.widget.CursorAdapter}, use
+ * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
+ * android.database.Cursor, int)} constructor <em>without</em> passing
+ * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
+ * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
+ * (that is, use 0 for the flags argument). This prevents the CursorAdapter
+ * from doing its own observing of the Cursor, which is not needed since
+ * when a change happens you will get a new Cursor throw another call
+ * here.
+ * <li> The Loader will release the data once it knows the application
+ * is no longer using it. For example, if the data is
+ * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
+ * you should not call close() on it yourself. If the Cursor is being placed in a
+ * {@link android.widget.CursorAdapter}, you should use the
+ * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
+ * method so that the old Cursor is not closed.
+ * </ul>
+ *
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
public void onLoadFinished(Loader<D> loader, D data);
+
+ /**
+ * Called when a previously created loader is being reset, and thus
+ * making its data unavailable. The application should at this point
+ * remove any references it has to the Loader's data.
+ *
+ * @param loader The Loader that is being reset.
+ */
+ public void onLoaderReset(Loader<D> loader);
}
/**
@@ -65,28 +115,52 @@
* will be called as the loader state changes. If at the point of call
* the caller is in its started state, and the requested loader
* already exists and has generated its data, then
- * callback. {@link LoaderCallbacks#onLoadFinished} will
+ * callback {@link LoaderCallbacks#onLoadFinished} will
* be called immediately (inside of this function), so you must be prepared
* for this to happen.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> initLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
/**
- * Creates a new loader in this manager, registers the callbacks to it,
+ * Starts a new or restarts an existing {@link android.content.Loader} in
+ * this manager, registers the callbacks to it,
* and (if the activity/fragment is currently started) starts loading it.
* If a loader with the same id has previously been
* started it will automatically be destroyed when the new loader completes
* its work. The callback will be delivered before the old loader
* is destroyed.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> restartLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
/**
- * Stops and removes the loader with the given ID.
+ * Stops and removes the loader with the given ID. If this loader
+ * had previously reported data to the client through
+ * {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call
+ * will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}.
*/
- public abstract void stopLoader(int id);
+ public abstract void destroyLoader(int id);
+
+ /**
+ * @deprecated Renamed to {@link #destroyLoader}.
+ */
+ @Deprecated
+ public void stopLoader(int id) {
+ destroyLoader(id);
+ }
/**
* Return the Loader with the given id or null if no matching Loader
@@ -132,6 +206,7 @@
Loader<Object> mLoader;
Object mData;
boolean mStarted;
+ boolean mNeedReset;
boolean mRetaining;
boolean mRetainingStarted;
boolean mDestroyed;
@@ -191,13 +266,16 @@
stop();
}
}
- if (mStarted && mData != null) {
- // This loader was retained, and now at the point of
- // finishing the retain we find we remain started, have
- // our data, and the owner has a new callback... so
- // let's deliver the data now.
- callOnLoadFinished(mLoader, mData);
- }
+ }
+
+ if (mStarted && mData != null) {
+ // This loader has retained its data, either completely across
+ // a configuration change or just whatever the last data set
+ // was after being restarted from a stop, and now at the point of
+ // finishing the retain we find we remain started, have
+ // our data, and the owner has a new callback... so
+ // let's deliver the data now.
+ callOnLoadFinished(mLoader, mData);
}
}
@@ -211,20 +289,35 @@
mLoader.unregisterListener(this);
mLoader.stopLoading();
}
- mData = null;
}
}
void destroy() {
if (DEBUG) Log.v(TAG, " Destroying: " + this);
mDestroyed = true;
+ if (mCallbacks != null && mLoader != null && mData != null && mNeedReset) {
+ String lastBecause = null;
+ if (mActivity != null) {
+ lastBecause = mActivity.mFragments.mNoTransactionsBecause;
+ mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+ }
+ try {
+ mCallbacks.onLoaderReset(mLoader);
+ } finally {
+ if (mActivity != null) {
+ mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ }
+ }
+ }
+ mNeedReset = false;
mCallbacks = null;
+ mData = null;
if (mLoader != null) {
if (mListenerRegistered) {
mListenerRegistered = false;
mLoader.unregisterListener(this);
}
- mLoader.destroy();
+ mLoader.reset();
}
}
@@ -238,7 +331,9 @@
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
mData = data;
- callOnLoadFinished(loader, data);
+ if (mStarted) {
+ callOnLoadFinished(loader, data);
+ }
if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
@@ -248,6 +343,7 @@
// clean it up.
LoaderInfo info = mInactiveLoaders.get(mId);
if (info != null && info != this) {
+ info.mNeedReset = false;
info.destroy();
mInactiveLoaders.remove(mId);
}
@@ -267,6 +363,7 @@
mActivity.mFragments.mNoTransactionsBecause = lastBecause;
}
}
+ mNeedReset = true;
}
}
@@ -303,7 +400,8 @@
writer.print(prefix); writer.print("mData="); writer.println(mData);
writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
writer.print(" mRetaining="); writer.print(mRetaining);
- writer.print(" mDestroyed="); writer.print(mDestroyed);
+ writer.print(" mDestroyed="); writer.println(mDestroyed);
+ writer.print(prefix); writer.print("mNeedReset="); writer.print(mNeedReset);
writer.print(" mListenerRegistered="); writer.println(mListenerRegistered);
}
}
@@ -366,6 +464,7 @@
// yet destroyed the last inactive loader. So just do
// that now.
if (DEBUG) Log.v(TAG, " Removing last inactive loader in " + this);
+ inactive.mNeedReset = false;
inactive.destroy();
mInactiveLoaders.put(id, info);
} else {
@@ -388,7 +487,7 @@
return (Loader<D>)info.mLoader;
}
- public void stopLoader(int id) {
+ public void destroyLoader(int id) {
if (DEBUG) Log.v(TAG, "stopLoader in " + this + " of " + id);
int idx = mLoaders.indexOfKey(id);
if (idx >= 0) {
@@ -396,6 +495,12 @@
mLoaders.removeAt(idx);
info.destroy();
}
+ idx = mInactiveLoaders.indexOfKey(id);
+ if (idx >= 0) {
+ LoaderInfo info = mInactiveLoaders.valueAt(idx);
+ mInactiveLoaders.removeAt(idx);
+ info.destroy();
+ }
}
@SuppressWarnings("unchecked")
@@ -416,12 +521,13 @@
return;
}
+ mStarted = true;
+
// Call out to sub classes so they can start their loaders
// Let the existing loaders know that we want to be notified when a load is complete
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).start();
}
- mStarted = true;
}
void doStop() {
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
deleted file mode 100644
index f0f5856..0000000
--- a/core/java/android/app/LoaderManagingFragment.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.content.Loader;
-import android.os.Bundle;
-
-import java.util.HashMap;
-
-/**
- * A Fragment that has utility methods for managing {@link Loader}s.
- *
- * @param <D> The type of data returned by the Loader. If you're using multiple Loaders with
- * different return types use Object and case the results.
- *
- * @deprecated This was an old design, it will be removed before Honeycomb ships.
- */
-@Deprecated
-public abstract class LoaderManagingFragment<D> extends Fragment
- implements Loader.OnLoadCompleteListener<D> {
- private boolean mStarted = false;
-
- static final class LoaderInfo<D> {
- public Bundle args;
- public Loader<D> loader;
- }
- private HashMap<Integer, LoaderInfo<D>> mLoaders;
- private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
-
- /**
- * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
- * If a loader with the same id has previously been started it will automatically be destroyed
- * when the new loader completes it's work. The callback will be delivered before the old loader
- * is destroyed.
- */
- public Loader<D> startLoading(int id, Bundle args) {
- LoaderInfo<D> info = mLoaders.get(id);
- if (info != null) {
- // Keep track of the previous instance of this loader so we can destroy
- // it when the new one completes.
- mInactiveLoaders.put(id, info);
- }
- info = new LoaderInfo<D>();
- info.args = args;
- mLoaders.put(id, info);
- Loader<D> loader = onCreateLoader(id, args);
- info.loader = loader;
- if (mStarted) {
- // The activity will start all existing loaders in it's onStart(), so only start them
- // here if we're past that point of the activitiy's life cycle
- loader.registerListener(id, this);
- loader.startLoading();
- }
- return loader;
- }
-
- protected abstract Loader<D> onCreateLoader(int id, Bundle args);
- protected abstract void onInitializeLoaders();
- protected abstract void onLoadFinished(Loader<D> loader, D data);
-
- public final void onLoadComplete(Loader<D> loader, D data) {
- // Notify of the new data so the app can switch out the old data before
- // we try to destroy it.
- onLoadFinished(loader, data);
-
- // Look for an inactive loader and destroy it if found
- int id = loader.getId();
- LoaderInfo<D> info = mInactiveLoaders.get(id);
- if (info != null) {
- Loader<D> oldLoader = info.loader;
- if (oldLoader != null) {
- oldLoader.destroy();
- }
- mInactiveLoaders.remove(id);
- }
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- super.onCreate(savedState);
-
- if (mLoaders == null) {
- // Look for a passed along loader and create a new one if it's not there
-// TODO: uncomment once getLastNonConfigurationInstance method is available
-// mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
- if (mLoaders == null) {
- mLoaders = new HashMap<Integer, LoaderInfo<D>>();
- onInitializeLoaders();
- }
- }
- if (mInactiveLoaders == null) {
- mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- // Call out to sub classes so they can start their loaders
- // Let the existing loaders know that we want to be notified when a load is complete
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- int id = entry.getKey();
- if (loader == null) {
- loader = onCreateLoader(id, info.args);
- info.loader = loader;
- }
- loader.registerListener(id, this);
- loader.startLoading();
- }
-
- mStarted = true;
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
-
- // Let the loader know we're done with it
- loader.unregisterListener(this);
-
- // The loader isn't getting passed along to the next instance so ask it to stop loading
- if (!getActivity().isChangingConfigurations()) {
- loader.stopLoading();
- }
- }
-
- mStarted = false;
- }
-
- /* TO DO: This needs to be turned into a retained fragment.
- @Override
- public Object onRetainNonConfigurationInstance() {
- // Pass the loader along to the next guy
- Object result = mLoaders;
- mLoaders = null;
- return result;
- }
- **/
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mLoaders != null) {
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
- loader.destroy();
- }
- }
- }
-
- /**
- * Stops and removes the loader with the given ID.
- */
- public void stopLoading(int id) {
- if (mLoaders != null) {
- LoaderInfo<D> info = mLoaders.remove(id);
- if (info != null) {
- Loader<D> loader = info.loader;
- if (loader != null) {
- loader.unregisterListener(this);
- loader.destroy();
- }
- }
- }
- }
-
- /**
- * @return the Loader with the given id or null if no matching Loader
- * is found.
- */
- public Loader<D> getLoader(int id) {
- LoaderInfo<D> loaderInfo = mLoaders.get(id);
- if (loaderInfo != null) {
- return mLoaders.get(id).loader;
- }
- return null;
- }
-}
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 26af4eb..f9920c7 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
-import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -72,11 +71,7 @@
public TimePickerDialog(Context context,
OnTimeSetListener callBack,
int hourOfDay, int minute, boolean is24HourView) {
- this(context,
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
- ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
- : com.android.internal.R.style.Theme_Dialog_Alert,
- callBack, hourOfDay, minute, is24HourView);
+ this(context, 0, callBack, hourOfDay, minute, is24HourView);
}
/**
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 7776874..9e03c25 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -27,6 +27,8 @@
Cursor mCursor;
ForceLoadContentObserver mObserver;
boolean mStopped;
+ boolean mContentChanged;
+ boolean mReset;
Uri mUri;
String[] mProjection;
String mSelection;
@@ -57,7 +59,7 @@
/* Runs on the UI thread */
@Override
public void deliverResult(Cursor cursor) {
- if (mStopped) {
+ if (mReset) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
@@ -66,9 +68,12 @@
}
Cursor oldCursor = mCursor;
mCursor = cursor;
- super.deliverResult(cursor);
- if (oldCursor != null && !oldCursor.isClosed()) {
+ if (!mStopped) {
+ super.deliverResult(cursor);
+ }
+
+ if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
@@ -94,10 +99,13 @@
@Override
public void startLoading() {
mStopped = false;
+ mReset = false;
if (mCursor != null) {
deliverResult(mCursor);
- } else {
+ }
+ if (mCursor == null || mContentChanged) {
+ mContentChanged = false;
forceLoad();
}
}
@@ -107,11 +115,6 @@
*/
@Override
public void stopLoading() {
- if (mCursor != null && !mCursor.isClosed()) {
- mCursor.close();
- }
- mCursor = null;
-
// Attempt to cancel the current load task if possible.
cancelLoad();
@@ -120,6 +123,18 @@
}
@Override
+ public void onContentChanged() {
+ if (mStopped) {
+ // This loader has been stopped, so we don't want to load
+ // new data right now... but keep track of it changing to
+ // refresh later if we start again.
+ mContentChanged = true;
+ return;
+ }
+ super.onContentChanged();
+ }
+
+ @Override
public void onCancelled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
@@ -127,9 +142,16 @@
}
@Override
- public void destroy() {
+ public void reset() {
+ mReset = true;
+
// Ensure the loader is stopped
stopLoading();
+
+ if (mCursor != null && !mCursor.isClosed()) {
+ mCursor.close();
+ }
+ mCursor = null;
}
public Uri getUri() {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 22befa8..ca5ff24 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1845,7 +1845,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_USB_ANLG_HEADSET_PLUG =
- "android.intent.action.DOCK_HEADSET_PLUG";
+ "android.intent.action.USB_ANLG_HEADSET_PLUG";
/**
* Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
@@ -1860,7 +1860,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_USB_DGTL_HEADSET_PLUG =
- "android.intent.action.HDMI_AUDIO_PLUG";
+ "android.intent.action.USB_DGTL_HEADSET_PLUG";
/**
* Broadcast Action: A HMDI cable was plugged or unplugged
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 234096a..73d7103 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -128,7 +128,7 @@
* the data set and may deliver future callbacks if the source changes. Calling
* {@link #stopLoading} will stop the delivery of callbacks.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void startLoading();
@@ -141,22 +141,34 @@
/**
* Stops delivery of updates until the next time {@link #startLoading()} is called
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void stopLoading();
/**
- * Destroys the loader and frees its resources, making it unusable.
+ * Resets the state of the Loader. The Loader should at this point free
+ * all of its resources, since it may never be called again; however, its
+ * {@link #startLoading()} may later be called at which point it must be
+ * able to start running again.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
- public abstract void destroy();
+ public void reset() {
+ destroy();
+ }
+
+ /**
+ * @deprecated Old API, implement reset() now.
+ */
+ @Deprecated
+ public void destroy() {
+ }
/**
* Called when {@link ForceLoadContentObserver} detects a change. Calls {@link #forceLoad()}
* by default.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public void onContentChanged() {
forceLoad();
diff --git a/core/java/android/content/SyncActivityTooManyDeletes.java b/core/java/android/content/SyncActivityTooManyDeletes.java
new file mode 100644
index 0000000..350f35e
--- /dev/null
+++ b/core/java/android/content/SyncActivityTooManyDeletes.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import com.android.internal.R;
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Presents multiple options for handling the case where a sync was aborted because there
+ * were too many pending deletes. One option is to force the delete, another is to rollback
+ * the deletes, the third is to do nothing.
+ * @hide
+ */
+public class SyncActivityTooManyDeletes extends Activity
+ implements AdapterView.OnItemClickListener {
+
+ private long mNumDeletes;
+ private Account mAccount;
+ private String mAuthority;
+ private String mProvider;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ finish();
+ return;
+ }
+
+ mNumDeletes = extras.getLong("numDeletes");
+ mAccount = (Account) extras.getParcelable("account");
+ mAuthority = extras.getString("authority");
+ mProvider = extras.getString("provider");
+
+ // the order of these must match up with the constants for position used in onItemClick
+ CharSequence[] options = new CharSequence[]{
+ getResources().getText(R.string.sync_really_delete),
+ getResources().getText(R.string.sync_undo_deletes),
+ getResources().getText(R.string.sync_do_nothing)
+ };
+
+ ListAdapter adapter = new ArrayAdapter<CharSequence>(this,
+ android.R.layout.simple_list_item_1,
+ android.R.id.text1,
+ options);
+
+ ListView listView = new ListView(this);
+ listView.setAdapter(adapter);
+ listView.setItemsCanFocus(true);
+ listView.setOnItemClickListener(this);
+
+ TextView textView = new TextView(this);
+ CharSequence tooManyDeletesDescFormat =
+ getResources().getText(R.string.sync_too_many_deletes_desc);
+ textView.setText(String.format(tooManyDeletesDescFormat.toString(),
+ mNumDeletes, mProvider, mAccount.name));
+
+ final LinearLayout ll = new LinearLayout(this);
+ ll.setOrientation(LinearLayout.VERTICAL);
+ final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+ ll.addView(textView, lp);
+ ll.addView(listView, lp);
+
+ // TODO: consider displaying the icon of the account type
+// AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+// for (AuthenticatorDescription desc : descs) {
+// if (desc.type.equals(mAccount.type)) {
+// try {
+// final Context authContext = createPackageContext(desc.packageName, 0);
+// ImageView imageView = new ImageView(this);
+// imageView.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
+// ll.addView(imageView, lp);
+// } catch (PackageManager.NameNotFoundException e) {
+// }
+// break;
+// }
+// }
+
+ setContentView(ll);
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // the constants for position correspond to the items options array in onCreate()
+ if (position == 0) startSyncReallyDelete();
+ else if (position == 1) startSyncUndoDeletes();
+ finish();
+ }
+
+ private void startSyncReallyDelete() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+ ContentResolver.requestSync(mAccount, mAuthority, extras);
+ }
+
+ private void startSyncUndoDeletes() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+ ContentResolver.requestSync(mAccount, mAuthority, extras);
+ }
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 599429b..8b292c9 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -2157,9 +2157,7 @@
}
CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
- Intent clickIntent = new Intent();
- clickIntent.setClassName("com.android.providers.subscribedfeeds",
- "com.android.settings.SyncActivityTooManyDeletes");
+ Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
clickIntent.putExtra("account", account);
clickIntent.putExtra("authority", authority);
clickIntent.putExtra("provider", authorityName.toString());
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 0f96d6b..5d222d9 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -24,7 +24,7 @@
interface INfcTag
{
int close(int nativeHandle);
- int connect(int nativeHandle);
+ int connect(int nativeHandle, int technology);
int reconnect(int nativeHandle);
int[] getTechList(int nativeHandle);
byte[] getUid(int nativeHandle);
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index d042634..7404950 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -63,6 +63,8 @@
/*package*/ final Bundle[] mTechExtras;
/*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock
+ /*package*/ int mConnectedTechnology;
+
/**
* Hidden constructor to be used by NFC service and internal classes.
* @hide
@@ -76,6 +78,8 @@
// Ensure mTechExtras is as long as mTechList
mTechExtras = Arrays.copyOf(techListExtras, techList.length);
mServiceHandle = serviceHandle;
+
+ mConnectedTechnology = -1;
}
/**
@@ -244,4 +248,29 @@
return new Tag[size];
}
};
+
+ /*
+ * @hide
+ */
+ public synchronized void setConnectedTechnology(int technology) {
+ if (mConnectedTechnology == -1) {
+ mConnectedTechnology = technology;
+ } else {
+ throw new IllegalStateException("Close other technology first!");
+ }
+ }
+
+ /*
+ * @hide
+ */
+ public int getConnectedTechnology() {
+ return mConnectedTechnology;
+ }
+
+ /*
+ * @hide
+ */
+ public void setTechnologyDisconnected() {
+ mConnectedTechnology = -1;
+ }
}
diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java
index a50c10b..553f6ec 100644
--- a/core/java/android/nfc/technology/BasicTagTechnology.java
+++ b/core/java/android/nfc/technology/BasicTagTechnology.java
@@ -22,6 +22,7 @@
import android.nfc.INfcTag;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
+import android.nfc.ErrorCodes;
import android.os.RemoteException;
import android.util.Log;
@@ -101,6 +102,13 @@
return mTag;
}
+ public void checkConnected() {
+ if ((mTag.getConnectedTechnology() != getTechnologyId()) ||
+ (mTag.getConnectedTechnology() == -1)) {
+ throw new IllegalStateException("Call connect() first!");
+ }
+ }
+
/**
* <p>Requires {@link android.Manifest.permission#NFC} permission.
*/
@@ -144,8 +152,53 @@
*/
@Override
public void connect() throws IOException {
- //TODO(nxp): enforce exclusivity
- mIsConnected = true;
+ try {
+ int errorCode = mTagService.connect(mTag.getServiceHandle(), getTechnologyId());
+
+ if (errorCode == ErrorCodes.SUCCESS) {
+ // Store this in the tag object
+ mTag.setConnectedTechnology(getTechnologyId());
+ mIsConnected = true;
+ } else {
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ throw new IOException("NFC service died");
+ }
+ }
+
+ /**
+ * Re-connect to the {@link Tag} associated with this connection.
+ * <p>
+ * Reconnecting to a tag can be used to reset the state of the tag itself.
+ * This method blocks until the connection is re-established.
+ * <p>
+ * {@link #close} can be called from another thread to cancel this connection
+ * attempt.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ * @throws IOException if the target is lost, or connect canceled
+ */
+ @Override
+ public void reconnect() throws IOException {
+ if (!mIsConnected) {
+ throw new IllegalStateException("Technology not connected yet");
+ } else {
+ try {
+ int errorCode = mTagService.reconnect(mTag.getServiceHandle());
+
+ if (errorCode != ErrorCodes.SUCCESS) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ attemptDeadServiceRecovery(e);
+ throw new IOException("NFC service died");
+ }
+ }
}
/**
@@ -160,7 +213,6 @@
*/
@Override
public void close() {
- mIsConnected = false;
try {
/* Note that we don't want to physically disconnect the tag,
* but just reconnect to it to reset its state
@@ -168,6 +220,9 @@
mTagService.reconnect(mTag.getServiceHandle());
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
+ } finally {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
}
}
@@ -183,6 +238,8 @@
* @throws IOException if the target is lost or connection closed
*/
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true);
if (response == null) {
diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java
index 5346c67..118bff7 100644
--- a/core/java/android/nfc/technology/IsoDep.java
+++ b/core/java/android/nfc/technology/IsoDep.java
@@ -75,6 +75,8 @@
* @throws IOException, UnsupportedOperationException
*/
public void selectAid(byte[] aid) throws IOException, UnsupportedOperationException {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
index defdcf2..d5f0a31 100644
--- a/core/java/android/nfc/technology/MifareClassic.java
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -229,6 +229,8 @@
* Authenticate for a given sector.
*/
public boolean authenticateSector(int sector, byte[] key, boolean keyA) {
+ checkConnected();
+
byte[] cmd = new byte[12];
// First byte is the command
@@ -264,6 +266,8 @@
* @throws IOException
*/
public byte[] readBlock(int sector, int block) throws IOException {
+ checkConnected();
+
byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
byte[] blockread_cmd = { 0x30, addr }; // phHal_eMifareRead
@@ -300,6 +304,8 @@
*/
@Override
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
if (response == null) {
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
index 58d2645..7103b4d 100644
--- a/core/java/android/nfc/technology/MifareUltralight.java
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -66,11 +66,44 @@
* @throws IOException
*/
public byte[] readBlock(int block) throws IOException {
+ checkConnected();
+
byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead
return transceive(blockread_cmd);
}
/**
+ * @throws IOException
+ */
+ public byte[] readOTP() throws IOException {
+ checkConnected();
+
+ return readBlock(3); // OTP is at page 3
+ }
+
+ public void writePage(int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte[] pagewrite_cmd = new byte[data.length + 2];
+ pagewrite_cmd[0] = (byte) 0xA2;
+ pagewrite_cmd[1] = (byte) block;
+ System.arraycopy(data, 0, pagewrite_cmd, 2, data.length);
+
+ transceive(pagewrite_cmd);
+ }
+
+ public void writeBlock(int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte[] blockwrite_cmd = new byte[data.length + 2];
+ blockwrite_cmd[0] = (byte) 0xA0;
+ blockwrite_cmd[1] = (byte) block;
+ System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
+
+ transceive(blockwrite_cmd);
+ }
+
+ /**
* Send data to a tag and receive the response.
* <p>
* This method will block until the response is received. It can be canceled
@@ -83,6 +116,8 @@
*/
@Override
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
if (response == null) {
@@ -95,12 +130,4 @@
}
}
- /**
- * @throws IOException
- */
- /*
- public byte[] readOTP();
- public void writePage(int block, byte[] data);
- public void writeBlock(int block, byte[] data);
- */
}
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index 7e194aa..c45c97d 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -113,6 +113,8 @@
* and requires a connection.
*/
public NdefMessage getNdefMessage() throws IOException, FormatException {
+ checkConnected();
+
try {
int serviceHandle = mTag.getServiceHandle();
if (mTagService.isNdef(serviceHandle)) {
@@ -143,6 +145,8 @@
* @throws IOException
*/
public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+ checkConnected();
+
try {
int errorCode = mTagService.ndefWrite(mTag.getServiceHandle(), msg);
switch (errorCode) {
@@ -172,6 +176,8 @@
* @throws IOException
*/
public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
@@ -180,6 +186,8 @@
* @throws IOException
*/
public boolean makeReadonly() throws IOException {
+ checkConnected();
+
try {
int errorCode = mTagService.ndefMakeReadOnly(mTag.getServiceHandle());
switch (errorCode) {
@@ -205,11 +213,15 @@
* For NFC Forum Type 1 and 2 only.
*/
public void makeLowLevelReadonly() {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
@Override
public byte[] transceive(byte[] data) {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index bd21e58..899b95f 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -49,7 +49,9 @@
* NdefFormatable#format(NdefMessage)}
*/
public boolean canBeFormatted() throws IOException {
- throw new UnsupportedOperationException();
+ checkConnected();
+
+ throw new UnsupportedOperationException();
}
/**
@@ -57,6 +59,8 @@
* NdefMessage to be written on the tag.
*/
public void format(NdefMessage firstMessage) throws IOException, FormatException {
+ checkConnected();
+
try {
byte[] DEFAULT_KEY = {(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF};
@@ -97,6 +101,8 @@
@Override
public byte[] transceive(byte[] data) {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/TagTechnology.java b/core/java/android/nfc/technology/TagTechnology.java
index bef1cc4..62216c1 100644
--- a/core/java/android/nfc/technology/TagTechnology.java
+++ b/core/java/android/nfc/technology/TagTechnology.java
@@ -82,6 +82,11 @@
public void connect() throws IOException;
/**
+ * @throws IOException
+ */
+ public void reconnect() throws IOException;
+
+ /**
* Non-blocking. Immediately causes all blocking calls
* to throw IOException.
*/
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 507cfab..0c6ab9e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -98,6 +98,12 @@
public static final int NFC_UID = 1022;
/**
+ * Defines the GID for the group that allows write access to the internal media storage.
+ * @hide
+ */
+ public static final int MEDIA_RW_GID = 1023;
+
+ /**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
* to applications.
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index b93dfd8..3401cb1 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -68,4 +68,15 @@
* 59.
*/
public static final String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
+
+ /**
+ * Activity Extra: Optionally skip the application UI.
+ * <p>
+ * This value can be passed as an extra field to the Intent created with
+ * ACTION_SET_ALARM. If true, the application is asked to bypass any
+ * intermediate UI and instead pop a toast indicating the result then
+ * finish the Activity. If false, the application may display intermediate
+ * UI like a confirmation dialog or alarm settings. The default is false.
+ */
+ public static final String EXTRA_SKIP_UI = "android.intent.extra.alarm.SKIP_UI";
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 9e5abdc..f91db87 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1514,7 +1514,7 @@
}
handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
- if (connectPanDeviceNative(objectPath, "nap", "panu")) {
+ if (connectPanDeviceNative(objectPath, "nap")) {
log ("connecting to PAN");
return true;
} else {
@@ -1662,7 +1662,8 @@
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
ifcg.mask = InetAddress.getByName(BLUETOOTH_NETMASK);
- if (ifcg.addr == null) {
+
+ if (ifcg.addr == null || ifcg.addr.equals(InetAddress.getByName("0.0.0.0"))) {
ifcg.addr = InetAddress.getByName(address);
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
}
@@ -2884,7 +2885,7 @@
private native boolean disconnectInputDeviceNative(String path);
private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
- private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
+ private native boolean connectPanDeviceNative(String path, String dstRole);
private native boolean disconnectPanDeviceNative(String path);
private native int[] addReservedServiceRecordsNative(int[] uuuids);
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index d55a943..02c324c 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -82,7 +82,11 @@
/**
* Starts an activity that will prompt the user for speech, sends it through a
- * speech recognizer, and invokes and displays a web search result.
+ * speech recognizer, and invokes and either displays a web search result or triggers
+ * another type of action based on the user's speech.
+ *
+ * <p>If you want to avoid triggering any type of action besides web search, you can use
+ * the {@link #EXTRA_WEB_SEARCH_ONLY} extra.
*
* <p>Required extras:
* <ul>
@@ -95,6 +99,7 @@
* <li>{@link #EXTRA_LANGUAGE}
* <li>{@link #EXTRA_MAX_RESULTS}
* <li>{@link #EXTRA_PARTIAL_RESULTS}
+ * <li>{@link #EXTRA_WEB_SEARCH_ONLY}
* </ul>
*
* <p> Result extras (returned in the result, not to be specified in the request):
@@ -182,6 +187,13 @@
* will choose how many results to return. Must be an integer.
*/
public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
+
+ /**
+ * Optional boolean, to be used with {@link #ACTION_WEB_SEARCH}, to indicate whether to
+ * only fire web searches in response to a user's speech. The default is false, meaning
+ * that other types of actions can be taken based on the user's speech.
+ */
+ public static final String EXTRA_WEB_SEARCH_ONLY = "android.speech.extra.WEB_SEARCH_ONLY";
/**
* Optional boolean to indicate whether partial results should be returned by the recognizer
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d27e99d..75aebc9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8206,8 +8206,9 @@
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
- * called. When implementing a view, do not override this method; instead,
- * you should implement {@link #onDraw}.
+ * called. When implementing a view, implement {@link #onDraw} instead of
+ * overriding this method. If you do need to override this method, call
+ * the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5385cd9..8446a8f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -576,14 +576,16 @@
/**
* Set the width and height layout parameters of the window. The default
- * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT to
- * make a window that is not full-screen.
+ * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
+ * or an absolute value to make a window that is not full-screen.
*
* @param width The desired layout width of the window.
* @param height The desired layout height of the window.
+ *
+ * @see ViewGroup.LayoutParams#height
+ * @see ViewGroup.LayoutParams#width
*/
- public void setLayout(int width, int height)
- {
+ public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index f4ee2384..99be64b 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -44,7 +44,10 @@
* this component and if they can not be resolved by the cache, the HTTP headers
* are attached, as appropriate, to the request for revalidation of content. The
* class also manages the cache size.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+@Deprecated
public final class CacheManager {
private static final String LOGTAG = "cache";
@@ -85,7 +88,10 @@
* This class represents a resource retrieved from the HTTP cache.
* Instances of this class can be obtained by invoking the
* CacheManager.getCacheFile() method.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static class CacheResult {
// these fields are saved to the database
int httpStatusCode;
@@ -167,6 +173,13 @@
public void setEncoding(String encoding) {
this.encoding = encoding;
}
+
+ /**
+ * @hide
+ */
+ public void setContentLength(long contentLength) {
+ this.contentLength = contentLength;
+ }
}
/**
@@ -181,11 +194,6 @@
removeAllCacheFiles();
mClearCacheOnInit = false;
}
- // If we're using the Chrome HTTP stack, disable the cache.
- // Chrome has its own cache, and we don't provide programmatic access to it.
- if (JniUtil.useChromiumHttpStack()) {
- setCacheDisabled(true);
- }
}
/**
@@ -218,9 +226,12 @@
/**
* get the base directory of the cache. With localPath of the CacheResult,
* it identifies the cache file.
- *
+ *
* @return File The base directory of the cache.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static File getCacheFileBaseDir() {
return mBaseDir;
}
@@ -238,17 +249,16 @@
if (mDisabled) {
removeAllCacheFiles();
}
- if (!mDisabled && JniUtil.useChromiumHttpStack()) {
- Log.w(LOGTAG, "CacheManager enabled, but it will not work as "
- + "expected because the Chrome HTTP stack is in use.");
- }
}
/**
* get the state of the current cache, enabled or disabled
- *
+ *
* @return return if it is disabled
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static boolean cacheDisabled() {
return mDisabled;
}
@@ -316,8 +326,11 @@
* HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in the
* cached headers.
*
- * @return the CacheResult for a given url
+ * @return the CacheResult for a given url.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static CacheResult getCacheFile(String url,
Map<String, String> headers) {
return getCacheFile(url, 0, headers);
@@ -392,7 +405,10 @@
* @return CacheResult for a given url
* @hide - hide createCacheFile since it has a parameter of type headers, which is
* in a hidden package.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static CacheResult createCacheFile(String url, int statusCode,
Headers headers, String mimeType, boolean forceCache) {
return createCacheFile(url, statusCode, headers, mimeType, 0,
@@ -457,7 +473,10 @@
/**
* Save the info of a cache file for a given url to the CacheMap so that it
* can be reused later
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static void saveCacheFile(String url, CacheResult cacheRet) {
saveCacheFile(url, 0, cacheRet);
}
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 8de30c5..ca3515c5 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -25,6 +25,7 @@
private static String sDatabaseDirectory;
private static String sCacheDirectory;
private static Boolean sUseChromiumHttpStack;
+ private static Context sContext;
private static boolean initialized = false;
@@ -38,9 +39,7 @@
if (initialized)
return;
- Context appContext = context.getApplicationContext();
- sDatabaseDirectory = appContext.getDatabasePath("dummy").getParent();
- sCacheDirectory = appContext.getCacheDir().getAbsolutePath();
+ sContext = context;
initialized = true;
}
@@ -50,6 +49,10 @@
*/
private static synchronized String getDatabaseDirectory() {
checkIntialized();
+
+ if (sDatabaseDirectory == null)
+ sDatabaseDirectory = sContext.getDatabasePath("dummy").getParent();
+
return sDatabaseDirectory;
}
@@ -59,6 +62,10 @@
*/
private static synchronized String getCacheDirectory() {
checkIntialized();
+
+ if (sCacheDirectory == null)
+ sCacheDirectory = sContext.getCacheDir().getAbsolutePath();
+
return sCacheDirectory;
}
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 54c9d9a..86a67c7 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -34,7 +34,11 @@
}
void finish() {
- mActionMode.finish();
+ // It is possible that onCreateActionMode was never called, in the case
+ // where there is no ActionBar, for example.
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
}
// ActionMode.Callback implementation
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index bbfcc08..05bb19d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3790,7 +3790,9 @@
public boolean selectText() {
int x = viewToContentX((int) mLastTouchX + mScrollX);
int y = viewToContentY((int) mLastTouchY + mScrollY);
- setUpSelect();
+ if (!setUpSelect()) {
+ return false;
+ }
if (mNativeClass != 0 && nativeWordSelection(x, y)) {
nativeSetExtendSelection();
mDrawSelectionPointer = false;
@@ -3818,17 +3820,21 @@
*/
private SelectActionModeCallback mSelectCallback;
+ // These values are possible options for didUpdateWebTextViewDimensions.
+ private static final int FULLY_ON_SCREEN = 0;
+ private static final int INTERSECTS_SCREEN = 1;
+ private static final int ANYWHERE = 2;
+
/**
* Check to see if the focused textfield/textarea is still on screen. If it
* is, update the the dimensions and location of WebTextView. Otherwise,
* remove the WebTextView. Should be called when the zoom level changes.
- * @param allowIntersect Whether to consider the textfield/textarea on
- * screen if it only intersects the screen (as opposed to being
- * completely on screen).
+ * @param intersection How to determine whether the textfield/textarea is
+ * still on screen.
* @return boolean True if the textfield/textarea is still on screen and the
* dimensions/location of WebTextView have been updated.
*/
- private boolean didUpdateWebTextViewDimensions(boolean allowIntersect) {
+ private boolean didUpdateWebTextViewDimensions(int intersection) {
Rect contentBounds = nativeFocusCandidateNodeBounds();
Rect vBox = contentToViewRect(contentBounds);
Rect visibleRect = new Rect();
@@ -3836,8 +3842,22 @@
// If the textfield is on screen, place the WebTextView in
// its new place, accounting for our new scroll/zoom values,
// and adjust its textsize.
- if (allowIntersect ? Rect.intersects(visibleRect, vBox)
- : visibleRect.contains(vBox)) {
+ boolean onScreen;
+ switch (intersection) {
+ case FULLY_ON_SCREEN:
+ onScreen = visibleRect.contains(vBox);
+ break;
+ case INTERSECTS_SCREEN:
+ onScreen = Rect.intersects(visibleRect, vBox);
+ break;
+ case ANYWHERE:
+ onScreen = true;
+ break;
+ default:
+ throw new AssertionError(
+ "invalid parameter passed to didUpdateWebTextViewDimensions");
+ }
+ if (onScreen) {
mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
vBox.height());
mWebTextView.updateTextSize();
@@ -3874,7 +3894,7 @@
private void onZoomAnimationEnd() {
// adjust the edit text view if needed
- if (inEditingMode() && didUpdateWebTextViewDimensions(false)
+ if (inEditingMode() && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)
&& nativeFocusCandidateIsPassword()) {
// If it is a password field, start drawing the WebTextView once
// again.
@@ -4011,7 +4031,7 @@
// finishes. We also do not need to do this unless the WebTextView
// is showing.
if (!animateZoom && inEditingMode()) {
- didUpdateWebTextViewDimensions(true);
+ didUpdateWebTextViewDimensions(ANYWHERE);
}
}
}
@@ -4116,7 +4136,7 @@
if (inEditingMode()) {
imm.showSoftInput(mWebTextView, 0);
if (zoom) {
- didUpdateWebTextViewDimensions(true);
+ didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
}
return;
}
@@ -4657,10 +4677,15 @@
return false;
}
- private void setUpSelect() {
- if (0 == mNativeClass) return; // client isn't initialized
- if (inFullScreenMode()) return;
- if (mSelectingText) return;
+ /*
+ * Enter selecting text mode. Returns true if the WebView is now in
+ * selecting text mode (including if it was already in that mode, and this
+ * method did nothing).
+ */
+ private boolean setUpSelect() {
+ if (0 == mNativeClass) return false; // client isn't initialized
+ if (inFullScreenMode()) return false;
+ if (mSelectingText) return true;
mExtendSelection = false;
mSelectingText = mDrawSelectionPointer = true;
// don't let the picture change during text selection
@@ -4680,7 +4705,13 @@
nativeHideCursor();
mSelectCallback = new SelectActionModeCallback();
mSelectCallback.setWebView(this);
- startActionMode(mSelectCallback);
+ if (startActionMode(mSelectCallback) == null) {
+ // There is no ActionMode, so do not allow the user to modify a
+ // selection.
+ selectionDone();
+ return false;
+ }
+ return true;
}
/**
@@ -4697,7 +4728,9 @@
void selectAll() {
if (0 == mNativeClass) return; // client isn't initialized
if (inFullScreenMode()) return;
- if (!mSelectingText) setUpSelect();
+ if (!mSelectingText && !setUpSelect()) {
+ return;
+ }
nativeSelectAll();
mDrawSelectionPointer = false;
mExtendSelection = true;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b8c8913..fdd0710 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1991,6 +1991,13 @@
.obtainMessage(WebCoreThread.RESUME_PRIORITY));
}
+ static void sendStaticMessage(int messageType, Object argument) {
+ if (sWebCoreHandler == null)
+ return;
+
+ sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
+ }
+
static void pauseUpdatePicture(WebViewCore core) {
// Note: there is one possible failure mode. If pauseUpdatePicture() is
// called from UI thread while WEBKIT_DRAW is just pulled out of the
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 6309cac..0deb0a0 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5184,6 +5184,8 @@
public void onRemoteAdapterConnected() {
if (mRemoteAdapter != mAdapter) {
setAdapter(mRemoteAdapter);
+ } else if (mRemoteAdapter != null) {
+ mRemoteAdapter.superNotifyDataSetChanged();
}
}
@@ -5191,10 +5193,11 @@
* Called back when the adapter disconnects from the RemoteViewsService.
*/
public void onRemoteAdapterDisconnected() {
- if (mRemoteAdapter == mAdapter) {
- mRemoteAdapter = null;
- setAdapter(null);
- }
+ // If the remote adapter disconnects, we keep it around
+ // since the currently displayed items are still cached.
+ // Further, we want the service to eventually reconnect
+ // when necessary, as triggered by this view requesting
+ // items from the Adapter.
}
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 162b030..ec8e93c 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -964,6 +964,8 @@
public void onRemoteAdapterConnected() {
if (mRemoteViewsAdapter != mAdapter) {
setAdapter(mRemoteViewsAdapter);
+ } else if (mRemoteViewsAdapter != null) {
+ mRemoteViewsAdapter.superNotifyDataSetChanged();
}
}
@@ -971,10 +973,11 @@
* Called back when the adapter disconnects from the RemoteViewsService.
*/
public void onRemoteAdapterDisconnected() {
- if (mRemoteViewsAdapter != mAdapter) {
- mRemoteViewsAdapter = null;
- setAdapter(mRemoteViewsAdapter);
- }
+ // If the remote adapter disconnects, we keep it around
+ // since the currently displayed items are still cached.
+ // Further, we want the service to eventually reconnect
+ // when necessary, as triggered by this view requesting
+ // items from the Adapter.
}
public void advance() {
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 4cf8785..516162a 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -67,7 +67,7 @@
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
- protected DataSetObserver mDataSetObserver = new MyDataSetObserver();
+ protected DataSetObserver mDataSetObserver;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -81,54 +81,81 @@
/**
* If set the adapter will call requery() on the cursor whenever a content change
- * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*/
+ @Deprecated
public static final int FLAG_AUTO_REQUERY = 0x01;
/**
* If set the adapter will register a content observer on the cursor and will call
- * {@link #onContentChanged()} when a notification comes in.
+ * {@link #onContentChanged()} when a notification comes in. Be careful when
+ * using this flag: you will need to unset the current Cursor from the adapter
+ * to avoid leaks due to its registered observers. This flag is not needed
+ * when using a CursorAdapter with a
+ * {@link android.content.CursorLoader}.
*/
public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
/**
- * Constructor. The adapter will call requery() on the cursor whenever
- * it changes so that the most recent data is always displayed.
+ * Constructor that always enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param c The cursor from which to get the data.
* @param context The context
*/
+ @Deprecated
public CursorAdapter(Context context, Cursor c) {
init(context, c, FLAG_AUTO_REQUERY);
}
/**
- * Constructor
+ * Constructor that allows control over auto-requery. It is recommended
+ * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
+ *
* @param c The cursor from which to get the data.
* @param context The context
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
/**
- * Constructor
+ * Recommended constructor.
+ *
* @param c The cursor from which to get the data.
* @param context The context
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter; may
+ * be any combination of {@link #FLAG_AUTO_REQUERY} and
+ * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
*/
public CursorAdapter(Context context, Cursor c, int flags) {
init(context, c, flags);
}
+ /**
+ * @deprecated Don't use this, use the normal constructor. This will
+ * be removed in the future.
+ */
+ @Deprecated
protected void init(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
- protected void init(Context context, Cursor c, int flags) {
+ void init(Context context, Cursor c, int flags) {
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
mAutoRequery = true;
@@ -142,13 +169,15 @@
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
mChangeObserver = new ChangeObserver();
+ mDataSetObserver = new MyDataSetObserver();
} else {
mChangeObserver = null;
+ mDataSetObserver = null;
}
if (cursorPresent) {
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
- c.registerDataSetObserver(mDataSetObserver);
+ if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
}
}
@@ -275,22 +304,39 @@
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*
- * @param cursor the new cursor to be used
+ * @param cursor The new cursor to be used
*/
public void changeCursor(Cursor cursor) {
- if (cursor == mCursor) {
- return;
+ Cursor old = swapCursor(cursor);
+ if (old != null) {
+ old.close();
}
- if (mCursor != null) {
- if (mChangeObserver != null) mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
+ }
+
+ /**
+ * Swap in a new Cursor, returning the old Cursor. Unlike
+ * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
+ * closed.
+ *
+ * @param newCursor The new cursor to be used.
+ * @return Returns the previously set Cursor, or null if there wasa not one.
+ * If the given new Cursor is the same instance is the previously set
+ * Cursor, null is also returned.
+ */
+ public Cursor swapCursor(Cursor newCursor) {
+ if (newCursor == mCursor) {
+ return null;
}
- mCursor = cursor;
- if (cursor != null) {
- if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
+ Cursor oldCursor = mCursor;
+ if (oldCursor != null) {
+ if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
+ if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
+ }
+ mCursor = newCursor;
+ if (newCursor != null) {
+ if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
+ if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
+ mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
@@ -300,6 +346,7 @@
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
+ return oldCursor;
}
/**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 3703846..6894c99 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -103,7 +103,7 @@
Drawable mDivider;
int mDividerHeight;
-
+
Drawable mOverScrollHeader;
Drawable mOverScrollFooter;
@@ -1535,7 +1535,6 @@
// reset the focus restoration
View focusLayoutRestoreDirectChild = null;
-
// Don't put header or footer views into the Recycler. Those are
// already cached in mHeaderViews;
if (dataChanged) {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7b35b51..8c22f97 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -534,7 +534,7 @@
});
// create the fling and adjust scrollers
- mFlingScroller = new Scroller(getContext());
+ mFlingScroller = new Scroller(getContext(), null, true);
mAdjustScroller = new Scroller(getContext(), new OvershootInterpolator());
updateInputTextView();
@@ -798,6 +798,7 @@
*/
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
+ resetSelectorIndices();
}
/**
@@ -979,10 +980,7 @@
// children
// after we have completed drawing ourselves.
- // Draw the selector wheel if needed
- if (mDrawSelectorWheel) {
- super.draw(canvas);
- }
+ super.draw(canvas);
// Draw our children if we are not showing the selector wheel of fading
// it out
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 52d9c08..a7bff62 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -148,11 +148,6 @@
adapter.mMainQueue.removeMessages(0);
adapter.mWorkerQueue.removeMessages(0);
- // Clear the cache (the meta data will be re-requested on service re-connection)
- synchronized (adapter.mCache) {
- adapter.mCache.reset();
- }
-
final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
if (callback != null) {
callback.onRemoteAdapterDisconnected();
@@ -335,8 +330,8 @@
// Compose the loading view text
TextView loadingTextView = (TextView) mLayoutInflater.inflate(
- com.android.internal.R.layout.remote_views_adapter_default_loading_view,
- layout, false);
+ com.android.internal.R.layout.remote_views_adapter_default_loading_view,
+ layout, false);
loadingTextView.setHeight(mFirstViewHeight);
loadingTextView.setTag(new Integer(0));
@@ -880,7 +875,7 @@
// a chance to update itself and return new meta data associated with the new data.
}
- private void superNotifyDataSetChanged() {
+ void superNotifyDataSetChanged() {
super.notifyDataSetChanged();
}
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index aee411e..7341c2c 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -35,13 +35,19 @@
private LayoutInflater mInflater;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
*/
+ @Deprecated
public ResourceCursorAdapter(Context context, int layout, Cursor c) {
super(context, c);
mLayout = mDropDownLayout = layout;
@@ -49,7 +55,11 @@
}
/**
- * Constructor.
+ * Constructor with default behavior as per
+ * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
+ * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
@@ -58,7 +68,7 @@
* @param c The cursor from which to get the data.
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
@@ -67,14 +77,15 @@
}
/**
- * Constructor.
+ * Standard constructor.
*
* @param context The context where the ListView associated with this adapter is running
- * @param layout resource identifier of a layout file that defines the views
+ * @param layout Resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
* @param c The cursor from which to get the data.
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
super(context, c, flags);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index e499e54..dcd58b0 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -18,6 +18,8 @@
import static android.widget.SuggestionsAdapter.getColumnString;
+import com.android.internal.R;
+
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.SearchableInfo;
@@ -44,14 +46,11 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView.OnEditorActionListener;
-import com.android.internal.R;
-
import java.util.WeakHashMap;
/**
@@ -84,7 +83,7 @@
private CursorAdapter mSuggestionsAdapter;
private View mSearchButton;
private View mSubmitButton;
- private View mCloseButton;
+ private ImageView mCloseButton;
private View mSearchEditFrame;
private View mVoiceButton;
private SearchAutoComplete mQueryTextView;
@@ -189,7 +188,7 @@
mSearchEditFrame = findViewById(R.id.search_edit_frame);
mSubmitButton = findViewById(R.id.search_go_btn);
- mCloseButton = findViewById(R.id.search_close_btn);
+ mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
mVoiceButton = findViewById(R.id.search_voice_btn);
mSearchButton.setOnClickListener(mOnClickListener);
@@ -252,7 +251,9 @@
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
if (mClearingFocus || isIconified()) return false;
- return mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+ boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+ if (result) updateViewsVisibility(mIconifiedByDefault);
+ return result;
}
/** @hide */
@@ -263,6 +264,7 @@
mQueryTextView.clearFocus();
setImeVisibility(false);
mClearingFocus = false;
+ updateViewsVisibility(mIconifiedByDefault);
}
/**
@@ -515,11 +517,21 @@
mSearchButton.setVisibility(visCollapsed);
mSubmitButton.setVisibility(mSubmitButtonEnabled && hasText ? visExpanded : GONE);
mSearchEditFrame.setVisibility(visExpanded);
+ updateCloseButton();
updateVoiceButton(!hasText);
requestLayout();
invalidate();
}
+ private void updateCloseButton() {
+ final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ // Should we show the close button? It is not shown if there's no focus,
+ // field is not iconified by default and there is no text in it.
+ final boolean showClose = hasText || mIconifiedByDefault || mQueryTextView.hasFocus();
+ mCloseButton.setVisibility(showClose ? VISIBLE : INVISIBLE);
+ mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ }
+
private void setImeVisibility(boolean visible) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -812,6 +824,7 @@
invalidate();
}
updateVoiceButton(!hasText);
+ updateCloseButton();
if (mOnQueryChangeListener != null) {
mOnQueryChangeListener.onQueryTextChanged(newText.toString());
}
@@ -882,6 +895,10 @@
}
}
+ void onTextFocusChanged() {
+ updateCloseButton();
+ }
+
private boolean onItemClicked(int position, int actionKey, String actionMsg) {
if (mOnSuggestionListener == null
|| !mOnSuggestionListener.onSuggestionClicked(position)) {
@@ -1283,6 +1300,12 @@
}
}
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ mSearchView.onTextFocusChanged();
+ }
+
/**
* We override this method so that we can allow a threshold of zero,
* which ACTV does not.
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index d1c2270..497610c 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -66,7 +66,23 @@
String[] mOriginalFrom;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
+ */
+ @Deprecated
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+ super(context, layout, c);
+ mTo = to;
+ mOriginalFrom = from;
+ findColumns(from);
+ }
+
+ /**
+ * Standard constructor.
*
* @param context The context where the ListView associated with this
* SimpleListItemFactory is running
@@ -80,9 +96,12 @@
* These should all be TextViews. The first N views in this list
* are given the values of the first N columns in the from
* parameter. Can be null if the cursor is not available yet.
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
- public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
- super(context, layout, c);
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
+ int[] to, int flags) {
+ super(context, layout, c, flags);
mTo = to;
mOriginalFrom = from;
findColumns(from);
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 11bdd65..ec2bb74 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -22,7 +22,6 @@
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
@@ -62,6 +61,11 @@
private static final float PERSPECTIVE_SHIFT_FACTOR_Y = 0.1f;
private static final float PERSPECTIVE_SHIFT_FACTOR_X = 0.1f;
+ private float mPerspectiveShiftX;
+ private float mPerspectiveShiftY;
+ private float mNewPerspectiveShiftX;
+ private float mNewPerspectiveShiftY;
+
@SuppressWarnings({"FieldCanBeLocal"})
private static final float PERSPECTIVE_SCALE_FACTOR = 0.f;
@@ -239,41 +243,54 @@
// Implement the faked perspective
if (toIndex != -1) {
- transformViewAtIndex(toIndex, view);
+ transformViewAtIndex(toIndex, view, true);
}
}
- private void transformViewAtIndex(int index, View view) {
- float maxPerspectiveShiftY = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR_Y;
- float maxPerspectiveShiftX = getMeasuredWidth() * PERSPECTIVE_SHIFT_FACTOR_X;
+ private void transformViewAtIndex(int index, final View view, boolean animate) {
+ final float maxPerspectiveShiftY = mPerspectiveShiftY;
+ final float maxPerspectiveShiftX = mPerspectiveShiftX;
index = mMaxNumActiveViews - index - 1;
if (index == mMaxNumActiveViews - 1) index--;
float r = (index * 1.0f) / (mMaxNumActiveViews - 2);
- float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
- PropertyValuesHolder scalePropX = PropertyValuesHolder.ofFloat("scaleX", scale);
- PropertyValuesHolder scalePropY = PropertyValuesHolder.ofFloat("scaleY", scale);
+ final float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
float perspectiveTranslationY = -stackDirection * r * maxPerspectiveShiftY;
float scaleShiftCorrectionY = stackDirection * (1 - scale) *
(getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR_Y) / 2.0f);
- float transY = perspectiveTranslationY + scaleShiftCorrectionY;
+ final float transY = perspectiveTranslationY + scaleShiftCorrectionY;
float perspectiveTranslationX = (1 - r) * maxPerspectiveShiftX;
float scaleShiftCorrectionX = (1 - scale) *
(getMeasuredWidth() * (1 - PERSPECTIVE_SHIFT_FACTOR_X) / 2.0f);
- float transX = perspectiveTranslationX + scaleShiftCorrectionX;
+ final float transX = perspectiveTranslationX + scaleShiftCorrectionX;
- PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", transX);
- PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
+ if (animate) {
+ PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", transX);
+ PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
+ PropertyValuesHolder scalePropX = PropertyValuesHolder.ofFloat("scaleX", scale);
+ PropertyValuesHolder scalePropY = PropertyValuesHolder.ofFloat("scaleY", scale);
- ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(view, scalePropX, scalePropY,
- translationY, translationX);
- pa.setDuration(100);
- pa.start();
+ ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(view, scalePropX, scalePropY,
+ translationY, translationX);
+ oa.setDuration(100);
+ view.setTagInternal(com.android.internal.R.id.viewAnimation, oa);
+ oa.start();
+ } else {
+ Object tag = view.getTag(com.android.internal.R.id.viewAnimation);
+ if (tag instanceof ObjectAnimator) {
+ ((ObjectAnimator) tag).cancel();
+ }
+
+ view.setTranslationX(transX);
+ view.setTranslationY(transY);
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ }
}
private void setupStackSlider(View v, int mode) {
@@ -369,7 +386,7 @@
for (int i = 0; i < getNumActiveViews(); i++) {
View v = getViewAtRelativeIndex(i);
if (v != null) {
- transformViewAtIndex(i, v);
+ transformViewAtIndex(i, v, false);
}
}
}
@@ -409,6 +426,13 @@
mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mSlideAmount);
mFirstLayoutHappened = true;
}
+
+ if (Float.compare(mPerspectiveShiftY, mNewPerspectiveShiftY) != 0 ||
+ Float.compare(mPerspectiveShiftX, mNewPerspectiveShiftX) != 0) {
+ mPerspectiveShiftY = mNewPerspectiveShiftY;
+ mPerspectiveShiftX = mNewPerspectiveShiftX;
+ updateChildTransforms();
+ }
}
@Override
@@ -922,15 +946,43 @@
private void measureChildren() {
final int count = getChildCount();
- final int childWidth = Math.round(getMeasuredWidth()*(1-PERSPECTIVE_SHIFT_FACTOR_X))
+
+ final int measuredWidth = getMeasuredWidth();
+ final int measuredHeight = getMeasuredHeight();
+
+ final int childWidth = Math.round(measuredWidth*(1-PERSPECTIVE_SHIFT_FACTOR_X))
- mPaddingLeft - mPaddingRight;
- final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR_Y))
+ final int childHeight = Math.round(measuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR_Y))
- mPaddingTop - mPaddingBottom;
+ int maxWidth = 0;
+ int maxHeight = 0;
+
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+ child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
+
+ if (child != mHighlight && child != mClickFeedback) {
+ final int childMeasuredWidth = child.getMeasuredWidth();
+ final int childMeasuredHeight = child.getMeasuredHeight();
+ if (childMeasuredWidth > maxWidth) {
+ maxWidth = childMeasuredWidth;
+ }
+ if (childMeasuredHeight > maxHeight) {
+ maxHeight = childMeasuredHeight;
+ }
+ }
+ }
+
+ mNewPerspectiveShiftX = PERSPECTIVE_SHIFT_FACTOR_X * measuredWidth;
+ mNewPerspectiveShiftY = PERSPECTIVE_SHIFT_FACTOR_Y * measuredHeight;
+ if (maxWidth > 0 && maxWidth < childWidth) {
+ mNewPerspectiveShiftX = measuredWidth - maxWidth;
+ }
+
+ if (maxHeight > 0 && maxHeight < childHeight) {
+ mNewPerspectiveShiftY = measuredHeight - maxHeight;
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 93a306e..d03b0ea 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -864,6 +864,7 @@
mInputType = EditorInfo.TYPE_NULL;
mInput = null;
bufferType = BufferType.SPANNABLE;
+ // Required to request focus while in touch mode.
setFocusableInTouchMode(true);
// So that selection can be changed using arrow keys and touch is handled.
setMovementMethod(ArrowKeyMovementMethod.getInstance());
@@ -912,9 +913,9 @@
setCompoundDrawablePadding(drawablePadding);
// Same as setSingleLine(), but make sure the transformation method and the maximum number
- // of lines of height (for multi-line only) are unchanged.
+ // of lines of height are unchanged for multi-line TextViews.
setInputTypeSingleLine(singleLine);
- applySingleLine(singleLine, false, false);
+ applySingleLine(singleLine, singleLine, singleLine);
if (singleLine && mInput == null && ellipsize < 0) {
ellipsize = 3; // END
@@ -2748,7 +2749,10 @@
*/
mText = text;
- if (mLinksClickable) {
+ // Do not change the movement method for text that support text selection as it
+ // would prevent an arbitrary cursor displacement.
+ final boolean hasTextSelection = this instanceof EditText || mTextIsSelectable;
+ if (mLinksClickable && !hasTextSelection) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
@@ -3876,7 +3880,7 @@
// - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would
// allow to test for hasSelection in onFocusChanged, which would trigger a
// startTextSelectionMode here. TODO
- if (this instanceof ExtractEditText && hasSelection() && hasSelectionController()) {
+ if (this instanceof ExtractEditText && hasSelection() && canSelectText()) {
startSelectionActionMode();
}
@@ -4073,7 +4077,7 @@
*
* Use {@link #setTextIsSelectable(boolean)} or the
* {@link android.R.styleable#TextView_textIsSelectable} XML attribute to make this TextView
- * selectable (the text is not selectable by default).
+ * selectable (text is not selectable by default).
*
* Note that the content of an EditText is always selectable.
*
@@ -4088,8 +4092,8 @@
/**
* Sets whether or not (default) the content of this view is selectable by the user.
*
- * Note that this methods affect the {@link #setFocusableInTouchMode(boolean)},
- * {@link #setFocusable(boolean)}, {@link #setClickable(boolean)} and
+ * Note that this methods affect the {@link #setFocusable(boolean)},
+ * {@link #setFocusableInTouchMode(boolean)} {@link #setClickable(boolean)} and
* {@link #setLongClickable(boolean)} states and you may want to restore these if they were
* customized.
*
@@ -7519,10 +7523,21 @@
return super.onKeyShortcut(keyCode, event);
}
+ /**
+ * Unlike {@link #textCanBeSelected()}, this method is based on the <i>current</i> state of the
+ * TextView. {@link #textCanBeSelected()} has to be true (this is one of the conditions to have
+ * a selection controller (see {@link #prepareCursorControllers()}), but this is not sufficient.
+ */
private boolean canSelectText() {
return hasSelectionController() && mText.length() != 0;
}
+ /**
+ * Test based on the <i>intrinsic</i> charateristics of the TextView.
+ * The text must be spannable and the movement method must allow for arbitary selection.
+ *
+ * See also {@link #canSelectText()}.
+ */
private boolean textCanBeSelected() {
// prepareCursorController() relies on this method.
// If you change this condition, make sure prepareCursorController is called anywhere
@@ -7675,12 +7690,44 @@
if (hasPasswordTransformationMethod()) {
// selectCurrentWord is not available on a password field and would return an
// arbitrary 10-charater selection around pressed position. Select all instead.
- // Note that cut/copy menu entries are not available for passwords.
- // This is however useful to delete or paste to replace the entire content.
+ // Cut/copy menu entries are not available for passwords, but being able to select all
+ // is however useful to delete or paste to replace the entire content.
selectAll();
return;
}
+ long lastTouchOffset = getLastTouchOffsets();
+ final int minOffset = extractRangeStartFromLong(lastTouchOffset);
+ final int maxOffset = extractRangeEndFromLong(lastTouchOffset);
+
+ int selectionStart, selectionEnd;
+
+ // If a URLSpan (web address, email, phone...) is found at that position, select it.
+ URLSpan[] urlSpans = ((Spanned) mText).getSpans(minOffset, maxOffset, URLSpan.class);
+ if (urlSpans.length == 1) {
+ URLSpan url = urlSpans[0];
+ selectionStart = ((Spanned) mText).getSpanStart(url);
+ selectionEnd = ((Spanned) mText).getSpanEnd(url);
+ } else {
+ long wordLimits = getWordLimitsAt(minOffset);
+ if (wordLimits >= 0) {
+ selectionStart = extractRangeStartFromLong(wordLimits);
+ } else {
+ selectionStart = Math.max(minOffset - 5, 0);
+ }
+
+ wordLimits = getWordLimitsAt(maxOffset);
+ if (wordLimits >= 0) {
+ selectionEnd = extractRangeEndFromLong(wordLimits);
+ } else {
+ selectionEnd = Math.min(maxOffset + 5, mText.length());
+ }
+ }
+
+ Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ }
+
+ private long getLastTouchOffsets() {
int minOffset, maxOffset;
if (mContextMenuTriggeredByKey) {
@@ -7692,23 +7739,7 @@
maxOffset = selectionController.getMaxTouchOffset();
}
- int selectionStart, selectionEnd;
-
- long wordLimits = getWordLimitsAt(minOffset);
- if (wordLimits >= 0) {
- selectionStart = extractRangeStartFromLong(wordLimits);
- } else {
- selectionStart = Math.max(minOffset - 5, 0);
- }
-
- wordLimits = getWordLimitsAt(maxOffset);
- if (wordLimits >= 0) {
- selectionEnd = extractRangeEndFromLong(wordLimits);
- } else {
- selectionEnd = Math.min(maxOffset + 5, mText.length());
- }
-
- Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ return packRangeInLong(minOffset, maxOffset);
}
@Override
@@ -7753,28 +7784,25 @@
boolean added = false;
mContextMenuTriggeredByKey = mDPadCenterIsDown || mEnterKeyIsDown;
// Problem with context menu on long press: the menu appears while the key in down and when
- // the key is released, the view does not receive the key_up event. This ensures that the
- // state is reset whenever the context menu action is displayed.
- // mContextMenuTriggeredByKey saved that state so that it is available in
- // onTextContextMenuItem. We cannot simply clear these flags in onTextContextMenuItem since
+ // the key is released, the view does not receive the key_up event.
+ // We need two layers of flags: mDPadCenterIsDown and mEnterKeyIsDown are set in key down/up
+ // events. We cannot simply clear these flags in onTextContextMenuItem since
// it may not be called (if the user/ discards the context menu with the back key).
+ // We clear these flags here and mContextMenuTriggeredByKey saves that state so that it is
+ // available in onTextContextMenuItem.
mDPadCenterIsDown = mEnterKeyIsDown = false;
MenuHandler handler = new MenuHandler();
if (mText instanceof Spanned) {
- int selStart = getSelectionStart();
- int selEnd = getSelectionEnd();
+ long lastTouchOffset = getLastTouchOffsets();
+ final int selStart = extractRangeStartFromLong(lastTouchOffset);
+ final int selEnd = extractRangeEndFromLong(lastTouchOffset);
- int min = Math.min(selStart, selEnd);
- int max = Math.max(selStart, selEnd);
-
- URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
- URLSpan.class);
- if (urls.length == 1) {
- menu.add(0, ID_COPY_URL, 0,
- com.android.internal.R.string.copyUrl).
- setOnMenuItemClickListener(handler);
+ URLSpan[] urls = ((Spanned) mText).getSpans(selStart, selEnd, URLSpan.class);
+ if (urls.length > 0) {
+ menu.add(0, ID_COPY_URL, 0, com.android.internal.R.string.copyUrl).
+ setOnMenuItemClickListener(handler);
added = true;
}
@@ -7786,7 +7814,7 @@
// populates the menu AFTER this call.
if (menu.size() > 0) {
menu.add(0, ID_SELECTION_MODE, 0, com.android.internal.R.string.selectTextMode).
- setOnMenuItemClickListener(handler);
+ setOnMenuItemClickListener(handler);
added = true;
}
@@ -7855,10 +7883,16 @@
setPrimaryClip(clip);
}
}
+ stopSelectionActionMode();
return true;
case ID_SELECTION_MODE:
- startSelectionActionMode();
+ if (mSelectionActionMode != null) {
+ // Selection mode is already started, simply change selected part.
+ updateSelectedRegion();
+ } else {
+ startSelectionActionMode();
+ }
return true;
}
@@ -7979,9 +8013,7 @@
startDrag(data, getTextThumbnailBuilder(selectedText), false, localState);
stopSelectionActionMode();
} else {
- // Start a new selection at current position, keep selectionAction mode on
- selectCurrentWord();
- getSelectionController().show();
+ updateSelectedRegion();
}
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mDiscardNextActionUp = true;
@@ -7998,6 +8030,17 @@
return false;
}
+ /**
+ * When selection mode is already started, this method simply updates the selected part of text
+ * to the text under the finger.
+ */
+ private void updateSelectedRegion() {
+ // Start a new selection at current position, keep selectionAction mode on
+ selectCurrentWord();
+ // Updates handles' positions
+ getSelectionController().show();
+ }
+
private boolean touchPositionIsInSelection() {
int selectionStart = getSelectionStart();
int selectionEnd = getSelectionEnd();
@@ -8063,6 +8106,11 @@
return false;
}
+ if (!canSelectText() || !requestFocus()) {
+ Log.w(LOG_TAG, "TextView does not support text selection. Action mode cancelled.");
+ return false;
+ }
+
selectCurrentWord();
final InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -8139,26 +8187,15 @@
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- if (!hasSelectionController()) {
- Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
- return false;
- }
-
- if (!requestFocus()) {
- return false;
- }
-
TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
mode.setSubtitle(null);
- if (canSelectText()) {
- menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+ menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
setAlphabeticShortcut('a').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- }
if (canCut()) {
menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 3a58867..633bdd3 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -505,12 +505,13 @@
}
mContainerView.setVisibility(View.VISIBLE);
mContainerView.setAlpha(0);
- mContainerView.setTranslationY(-mContainerView.getHeight());
AnimatorSet anim = new AnimatorSet();
- AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "translationY", 0))
- .with(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
if (mContentView != null) {
- b.with(ObjectAnimator.ofFloat(mContentView, "translationY", -mContainerView.getHeight(), 0));
+ b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
+ -mContainerView.getHeight(), 0));
+ mContainerView.setTranslationY(-mContainerView.getHeight());
+ b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
}
anim.addListener(mShowListener);
mCurrentAnim = anim;
@@ -527,11 +528,12 @@
}
mContainerView.setAlpha(1);
AnimatorSet anim = new AnimatorSet();
- AnimatorSet.Builder b = anim.play(
- ObjectAnimator.ofFloat(mContainerView, "translationY", -mContainerView.getHeight()))
- .with(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
if (mContentView != null) {
- b.with(ObjectAnimator.ofFloat(mContentView, "translationY", 0, -mContainerView.getHeight()));
+ b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
+ 0, -mContainerView.getHeight()));
+ b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
+ -mContainerView.getHeight()));
}
anim.addListener(mHideListener);
mCurrentAnim = anim;
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index b9ae526..2c39871 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -1154,7 +1154,7 @@
}
static jboolean connectPanDeviceNative(JNIEnv *env, jobject object, jstring path,
- jstring srcRole, jstring dstRole) {
+ jstring dstRole) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
LOGE("connectPanDeviceNative");
@@ -1165,7 +1165,6 @@
if (nat && eventLoopNat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
- const char *src = env->GetStringUTFChars(srcRole, NULL);
const char *dst = env->GetStringUTFChars(dstRole, NULL);
int len = env->GetStringLength(path) + 1;
@@ -1175,12 +1174,10 @@
bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
context_path, eventLoopNat, c_path,
DBUS_NETWORK_IFACE, "Connect",
- DBUS_TYPE_STRING, &src,
DBUS_TYPE_STRING, &dst,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
- env->ReleaseStringUTFChars(srcRole, src);
env->ReleaseStringUTFChars(dstRole, dst);
return ret ? JNI_TRUE : JNI_FALSE;
}
@@ -1274,7 +1271,7 @@
{"setBluetoothTetheringNative", "(ZLjava/lang/String;Ljava/lang/String;)Z",
(void *)setBluetoothTetheringNative},
- {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void *)connectPanDeviceNative},
{"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92b50c7..981661a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1335,6 +1335,11 @@
android:exported="true">
</activity>
+ <activity android:name="android.content.SyncActivityTooManyDeletes"
+ android:theme="@android:style/Theme.Holo.Dialog"
+ android:label="@string/sync_too_many_deletes">
+ </activity>
+
<activity android:name="com.android.server.ShutdownActivity"
android:permission="android.permission.SHUTDOWN"
android:excludeFromRecents="true">
diff --git a/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_dark.9.png b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_dark.9.png
rename to core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png
deleted file mode 100644
index 2edae8f..0000000
--- a/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_clear_off.png b/core/res/res/drawable-hdpi/ic_clear_disabled.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_clear_off.png
rename to core/res/res/drawable-hdpi/ic_clear_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_clear.png b/core/res/res/drawable-hdpi/ic_clear_normal.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_clear.png
rename to core/res/res/drawable-hdpi/ic_clear_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_clear_search_api_disabled_holo_light.png b/core/res/res/drawable-hdpi/ic_clear_search_api_disabled_holo_light.png
new file mode 100644
index 0000000..3edbd74
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_clear_search_api_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_clear_search_api_holo_light.png b/core/res/res/drawable-hdpi/ic_clear_search_api_holo_light.png
new file mode 100644
index 0000000..90db01b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_clear_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_commit_search_api_holo_light.png b/core/res/res/drawable-hdpi/ic_commit_search_api_holo_light.png
new file mode 100644
index 0000000..b01688f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_commit_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_go_search_api_holo_light.png b/core/res/res/drawable-hdpi/ic_go_search_api_holo_light.png
new file mode 100644
index 0000000..7e1ba2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_go_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_search_api_holo_light.png b/core/res/res/drawable-hdpi/ic_search_api_holo_light.png
new file mode 100644
index 0000000..72e207b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_voice_search_api_holo_light.png b/core/res/res/drawable-hdpi/ic_voice_search_api_holo_light.png
new file mode 100644
index 0000000..3481c98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_voice_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_dark.png
index 6fbd7d2..f43f9ad 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_light.png
index 3a4cdec..2ada3ef 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_dark.png
index b1c3991..5ed7040 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_light.png
index 6fbce8c..d4a01cf 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_focused_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_focused_holo_dark.png
index 3bb4c29..ada6251 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_focused_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_focused_holo_light.png
index 8f02162..1247c7a 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_dark.png
index 8f57d2c..3d13454 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_light.png
index df6f76b..4898244 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_longpressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_normal_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_normal_holo_dark.png
index a47bf31..bb1074c 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_normal_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_normal_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_normal_holo_light.png
index 04046aa..e6e5a0f 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_normal_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_dark.png
index b6021e0..b54e603 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_light.png b/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_light.png
index 0f38d6b..70ee54c 100644
--- a/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_down_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_dark.png
index 14a4e31..a55c96a 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_light.png
index 21a2ac1..4e31c72 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_dark.png
index 1a1da57..8158596 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_light.png
index a242c80..37e5c0d 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_focused_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_focused_holo_dark.png
index 50045e4..7ec94e6 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_focused_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_focused_holo_light.png
index 659b3c7..780fff1 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_dark.png
index 9112530..73c8dd9 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_light.png
index 21aa7f7..3b96480 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_longpressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_normal_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_normal_holo_dark.png
index d145975..21f0871 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_normal_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_normal_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_normal_holo_light.png
index 167bab7..49e3c15 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_normal_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_dark.png b/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_dark.png
index 2844c3f..a15a5f5 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_light.png b/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_light.png
index 9d83038..7441361 100644
--- a/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/timepicker_up_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_dark.9.png b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_dark.9.png
rename to core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png
deleted file mode 100644
index a8cfd77..0000000
--- a/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_clear_off.png b/core/res/res/drawable-mdpi/ic_clear_disabled.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_clear_off.png
rename to core/res/res/drawable-mdpi/ic_clear_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_clear.png b/core/res/res/drawable-mdpi/ic_clear_normal.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_clear.png
rename to core/res/res/drawable-mdpi/ic_clear_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_clear_search_api_disabled_holo_light.png b/core/res/res/drawable-mdpi/ic_clear_search_api_disabled_holo_light.png
new file mode 100644
index 0000000..3edbd74
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_clear_search_api_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_clear_search_api_holo_light.png b/core/res/res/drawable-mdpi/ic_clear_search_api_holo_light.png
new file mode 100644
index 0000000..90db01b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_clear_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_commit_search_api_holo_light.png b/core/res/res/drawable-mdpi/ic_commit_search_api_holo_light.png
new file mode 100644
index 0000000..b01688f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_commit_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_go_search_api_holo_light.png b/core/res/res/drawable-mdpi/ic_go_search_api_holo_light.png
new file mode 100644
index 0000000..7e1ba2a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_go_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_search_api_holo_light.png b/core/res/res/drawable-mdpi/ic_search_api_holo_light.png
new file mode 100644
index 0000000..72e207b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_voice_search_api_holo_light.png b/core/res/res/drawable-mdpi/ic_voice_search_api_holo_light.png
new file mode 100644
index 0000000..3481c98
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_voice_search_api_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_dark.png
index d86534c..113b369 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_light.png
index 6ae5d4b..e3c416e 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_dark.png
index fd578b6..e123db9 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_light.png
index a0caaa9..f93d082 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_focused_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_focused_holo_dark.png
index f6f4ed2..d8bd34f 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_focused_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_focused_holo_light.png
index 2591adb..0747f82 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_dark.png
index efee099..1c7abf8 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_light.png
index f7b09de..ec9d1f2 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_longpressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_normal_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_normal_holo_dark.png
index 76f13a6..d6259e2 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_normal_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_normal_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_normal_holo_light.png
index cb8e764..aac5f31 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_normal_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_dark.png
index 7c0d0bc..fd8076f 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_light.png b/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_light.png
index 9d7ff6b..c06ff40 100644
--- a/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_dark.png
index cfdfd174..d69615d 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_light.png
index 43bdf1d..eb7a283 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_dark.png
index 2ffe46b..1ee5510 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_light.png
index 51bb2d0..2269577 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_focused_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_focused_holo_dark.png
index dece157..9954130 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_focused_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_focused_holo_light.png
index 384cb32..8553d1c 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_dark.png
index 84ec4f7..b4ed2c5 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_light.png
index 318befc..2496fb8 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_longpressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_normal_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_normal_holo_dark.png
index d97a832..3bcdd71 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_normal_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_normal_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_normal_holo_light.png
index 19d75e5..1113de7 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_normal_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_dark.png b/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_dark.png
index 1189e5c..7303a19 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_light.png b/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_light.png
index 9f283ab..7abdbfc 100644
--- a/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable/ic_clear.xml b/core/res/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..f353496
--- /dev/null
+++ b/core/res/res/drawable/ic_clear.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:drawable="@drawable/ic_clear_disabled" />
+ <item
+ android:drawable="@drawable/ic_clear_normal" />
+</selector>
diff --git a/core/res/res/drawable/ic_clear_holo_light.xml b/core/res/res/drawable/ic_clear_holo_light.xml
new file mode 100644
index 0000000..8ba1e3c
--- /dev/null
+++ b/core/res/res/drawable/ic_clear_holo_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:drawable="@drawable/ic_clear_search_api_disabled_holo_light" />
+ <item
+ android:drawable="@drawable/ic_clear_search_api_holo_light" />
+</selector>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index e7783da..a5f87d3 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -34,7 +34,7 @@
android:orientation="vertical"
android:layout_width="0px"
android:layout_height="match_parent"
- android:layout_marginRight="0dp"
+ android:layout_marginRight="@dimen/preference_screen_side_margin_negative"
android:layout_marginLeft="@dimen/preference_screen_side_margin"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp"
@@ -61,7 +61,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="20"
- android:layout_marginLeft="-4dp"
+ android:layout_marginLeft="@dimen/preference_screen_side_margin"
android:layout_marginRight="@dimen/preference_screen_side_margin"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml
index fcdf91b..53906f9 100644
--- a/core/res/res/layout/search_dropdown_item_icons_2line.xml
+++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml
@@ -42,8 +42,8 @@
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
- android:src="@android:drawable/edit_query"
- android:background="@android:drawable/edit_query_background"
+ android:src="?attr/searchViewEditQuery"
+ android:background="?attr/searchViewEditQueryBackground"
android:visibility="gone" />
<ImageView android:id="@android:id/icon2"
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index 0fb824f..bb7f700 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -44,7 +44,7 @@
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
- android:src="@android:drawable/ic_search"
+ android:src="?android:attr/searchViewSearchIcon"
/>
<LinearLayout
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index b1f12b5..fbfc3bf 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -22,5 +22,6 @@
<dimen name="password_keyboard_key_height">47dip</dimen>
<dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
<dimen name="preference_screen_side_margin">96dp</dimen>
+ <dimen name="preference_screen_side_margin_negative">-100dp</dimen>
<dimen name="preference_widget_width">72dp</dimen>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index 8f590b7..7353f16 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -18,9 +18,10 @@
*/
-->
<resources>
- <dimen name="status_bar_height">48dip</dimen>
<!-- Height of the status bar -->
- <dimen name="status_bar_icon_size">48dip</dimen>
+ <dimen name="status_bar_height">48dip</dimen>
+ <!-- Width and height of a single notification icon in the status bar -->
+ <dimen name="status_bar_icon_size">32dip</dimen>
<!-- Size of the giant number (unread count) in the notifications -->
<dimen name="status_bar_content_number_size">48sp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 701b8ca..873f539 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -48,6 +48,7 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
+
<!-- =========== -->
<!-- Text styles -->
<!-- =========== -->
@@ -261,7 +262,7 @@
<!-- Flag indicating whether this is a translucent window. -->
<attr name="windowIsTranslucent" format="boolean" />
<!-- Flag indicating that this window's background should be the
- user's current wallpaper. -->
+ user's current wallpaper. -->
<attr name="windowShowWallpaper" format="boolean" />
<!-- This Drawable is overlaid over the foreground of the Window's content area, usually
to place a shadow below the title. -->
@@ -667,6 +668,10 @@
<attr name="searchViewSearchIcon" format="reference" />
<!-- SearchView Voice button icon -->
<attr name="searchViewVoiceIcon" format="reference" />
+ <!-- SearchView query refinement icon -->
+ <attr name="searchViewEditQuery" format="reference" />
+ <!-- SearchView query refinement icon background -->
+ <attr name="searchViewEditQueryBackground" format="reference" />
<!-- Specifies a drawable to use for the 'home as up' indicator. -->
<attr name="homeAsUpIndicator" format="reference" />
@@ -4306,7 +4311,7 @@
If not supplied, then no activity will be launched. -->
<attr name="configure" format="string" />
<!-- A preview of what the AppWidget will look like after it's configured.
- If not supplied, the AppWidget's icon will be used. -->
+ If not supplied, the AppWidget's icon will be used. -->
<attr name="previewImage" format="reference" />
<!-- The view id of the AppWidget subview which should be auto-advanced.
by the widget's host. -->
@@ -4417,6 +4422,10 @@
<attr name="smallIcon" format="reference"/>
<!-- A preferences.xml file for authenticator-specific settings. -->
<attr name="accountPreferences" format="reference"/>
+ <!-- Account handles its own token storage and permissions.
+ Default to false
+ -->
+ <attr name="customTokens" format="boolean"/>
</declare-styleable>
<!-- =============================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5d03638..0d2d42f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -497,6 +497,17 @@
to handle queries on each database. -->
<integer name="db_connection_pool_size">1</integer>
+ <!-- Max space (in MB) allocated to DownloadManager to store the downloaded
+ files if they are to be stored in DownloadManager's data dir,
+ which typically is /data/data/com.android.providers.downloads/files -->
+ <integer name="config_downloadDataDirSize">100</integer>
+
+ <!-- When the free space available in DownloadManager's data dir falls
+ below the percentage value specified by this param, DownloadManager
+ starts removing files to try to make percentage of available
+ free space above this threshold value. -->
+ <integer name="config_downloadDataDirLowSpaceThreshold">10</integer>
+
<!-- The URL that should be sent in an x-wap-profile header with an HTTP request,
as defined in the Open Mobile Alliance User Agent Profile specification
OMA-TS-UAProf-V2_0-20060206-A Section 8.1.1.1. If the URL contains a '%s'
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2f0dfc1..53d4e42 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -54,6 +54,8 @@
<dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
<!-- Preference activity side margins -->
<dimen name="preference_screen_side_margin">0dp</dimen>
+ <!-- Preference activity side margins negative-->
+ <dimen name="preference_screen_side_margin_negative">0dp</dimen>
<!-- Preference widget area width (to the left of the text) -->
<dimen name="preference_widget_width">56dp</dimen>
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7a0fede..93ccd4f 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -73,4 +73,5 @@
<item type="id" name="fillInIntent" />
<item type="id" name="rowTypeId" />
<item type="id" name="up" />
+ <item type="id" name="viewAnimation" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3a5b238..7e06c86 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1407,6 +1407,7 @@
<public type="attr" name="fastScrollPreviewBackgroundRight" />
<public type="attr" name="fastScrollTrackDrawable" />
<public type="attr" name="fastScrollOverlayPosition" />
+ <public type="attr" name="customTokens" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e48321c..92f3593 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -298,12 +298,12 @@
<string name="shutdown_confirm_question">Would you like to shut down?</string>
<!-- Recent Tasks dialog: title
- TODO: this should move to SystemUI.apk, but the code for the old
+ TODO: this should move to SystemUI.apk, but the code for the old
recent dialog is still in the framework
-->
<string name="recent_tasks_title">Recent</string>
<!-- Recent Tasks dialog: message when there are no recent applications
- TODO: this should move to SystemUI.apk, but the code for the old
+ TODO: this should move to SystemUI.apk, but the code for the old
recent dialog is still in the framework
-->
<string name="no_recent_tasks">No recent applications.</string>
@@ -1363,17 +1363,17 @@
<!-- Title of policy access to limiting the user's password choices -->
<string name="policylab_limitPassword">Set password rules</string>
<!-- Description of policy access to limiting the user's password choices -->
- <string name="policydesc_limitPassword">Control the length and the characters
+ <string name="policydesc_limitPassword">Control the length and the characters
allowed in screen-unlock passwords</string>
<!-- Title of policy access to watch user login attempts -->
<string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
<!-- Description of policy access to watch user login attempts -->
- <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords
- entered when unlocking the screen, and lock the tablet or erase all the tablet\'s
+ <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords
+ entered when unlocking the screen, and lock the tablet or erase all the tablet\'s
data if too many incorrect passwords are entered</string>
<!-- Description of policy access to watch user login attempts -->
- <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords
- entered when unlocking the screen, and lock the phone or erase all the phone\'s
+ <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords
+ entered when unlocking the screen, and lock the phone or erase all the phone\'s
data if too many incorrect passwords are entered</string>
<!-- Title of policy access to reset user's password -->
<string name="policylab_resetPassword">Change the screen-unlock password</string>
@@ -1386,10 +1386,10 @@
<!-- Title of policy access to wipe the user's data -->
<string name="policylab_wipeData">Erase all data</string>
<!-- Description of policy access to wipe the user's data -->
- <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning,
+ <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning,
by performing a factory data reset</string>
<!-- Description of policy access to wipe the user's data -->
- <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning,
+ <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning,
by performing a factory data reset</string>
<string name="policylab_setGlobalProxy">Set the device global proxy</string>
<!-- Description of policy access to wipe the user's data -->
@@ -1602,7 +1602,7 @@
<string name="relationTypeSister">Sister</string>
<!-- Spouse relationship type [CHAR LIMIT=20] -->
<string name="relationTypeSpouse">Spouse</string>
-
+
<!-- Custom SIP address type -->
<string name="sipAddressTypeCustom">Custom</string>
<!-- Home SIP address type -->
@@ -2640,4 +2640,15 @@
<!-- Network positioning verification No. Button to push to deny sharing of location
information. -->
<string name="gpsVerifNo">No</string>
+
+ <!-- Error message when the sync tried to delete too many things -->
+ <string name="sync_too_many_deletes">Delete limit exceeded</string>
+ <!-- Dialog message for when there are too many deletes that would take place and we want user confirmation -->
+ <string name="sync_too_many_deletes_desc">There are <xliff:g id="number_of_deleted_items">%1$d</xliff:g> deleted items for <xliff:g id="type_of_sync">%2$s</xliff:g>, account <xliff:g id="account_name">%3$s</xliff:g>. What would you like to do?</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to delete the items -->
+ <string name="sync_really_delete">Delete the items.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to undo the deletions -->
+ <string name="sync_undo_deletes">Undo the deletes.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to do nothing for now -->
+ <string name="sync_do_nothing">Do nothing for now.</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 3e75261..920a59e 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -488,7 +488,7 @@
<item name="android:otherMonthDateColor">#66FFFFFF</item>
<item name="android:weekNumberColor">#33FFFFFF</item>
<item name="android:gridLinesColor">#19FFFFFF</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_dark</item>
+ <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
</style>
<!-- @hide -->
@@ -1450,7 +1450,7 @@
<item name="android:otherMonthDateColor">#66FFFFFF</item>
<item name="android:weekNumberColor">#33FFFFFF</item>
<item name="android:gridLinesColor">#19FFFFFF</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_dark</item>
+ <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
</style>
<style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
@@ -1819,12 +1819,12 @@
<!-- @hide -->
<style name="Widget.Holo.Light.DayPickerWeekView" parent="Widget.DayPickerWeekView">
- <item name="android:selectionBackgroundColor">#7F080030</item>
+ <item name="android:selectionBackgroundColor">#330066ff</item>
<item name="android:focusedMonthDateColor">#FF000000</item>
<item name="android:otherMonthDateColor">#7F08002B</item>
<item name="android:weekNumberColor">#7F080021</item>
<item name="android:gridLinesColor">#7F08002A</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_light</item>
+ <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
</style>
<!-- @hide -->
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f0f101e..87029ef 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -269,6 +269,8 @@
<item name="searchViewSearchIcon">@android:drawable/ic_search</item>
<item name="searchViewGoIcon">@android:drawable/ic_go</item>
<item name="searchViewVoiceIcon">@android:drawable/ic_voice_search</item>
+ <item name="searchViewEditQuery">@android:drawable/ic_commit</item>
+ <item name="searchViewEditQueryBackground">?attr/selectableItemBackground</item>
<!-- PreferenceFrameLayout attributes -->
@@ -362,8 +364,13 @@
<item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_light</item>
<!-- SearchView attributes -->
<item name="searchDropdownBackground">@android:drawable/search_dropdown_light</item>
+ <item name="searchViewCloseIcon">@android:drawable/ic_clear_holo_light</item>
+ <item name="searchViewSearchIcon">@android:drawable/ic_search_api_holo_light</item>
+ <item name="searchViewGoIcon">@android:drawable/ic_go_search_api_holo_light</item>
+ <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search_api_holo_light</item>
+ <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_holo_light</item>
</style>
-
+
<!-- Variant of the light theme with no title bar -->
<style name="Theme.Light.NoTitleBar">
<item name="android:windowNoTitle">true</item>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index 6717bda..e0a3ee6 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -77,7 +77,7 @@
List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
boolean found = false;
for (WifiConfiguration c : configList) {
- if (c.networkId == netId) {
+ if (c.networkId == netId && c.SSID.equals(config.SSID)) {
found = true;
}
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index b1e38ee..f2857fa 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -36,6 +36,9 @@
android:description="@string/permdesc_testDenied" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+ <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
+ <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
+ <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 8df37ad..1bb2a57 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -16,8 +16,12 @@
package android.app;
+import coretestutils.http.MockResponse;
+import coretestutils.http.MockWebServer;
+
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest.DataType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -27,11 +31,10 @@
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager;
-import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.SystemClock;
import android.provider.Settings;
import android.test.InstrumentationTestCase;
import android.util.Log;
@@ -43,19 +46,12 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
-import java.util.concurrent.TimeoutException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
import java.util.Random;
import java.util.Set;
-import java.util.Vector;
-
-import junit.framework.AssertionFailedError;
-
-import coretestutils.http.MockResponse;
-import coretestutils.http.MockWebServer;
+import java.util.concurrent.TimeoutException;
/**
* Base class for Instrumented tests for the Download Manager.
@@ -67,7 +63,7 @@
protected String mFileType = "text/plain";
protected Context mContext = null;
protected MultipleDownloadsCompletedReceiver mReceiver = null;
- protected static final int DEFAULT_FILE_SIZE = 130 * 1024; // 130kb
+ protected static final int DEFAULT_FILE_SIZE = 10 * 1024; // 10kb
protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
@@ -85,6 +81,9 @@
protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
+ protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
+ protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
+
// Just a few popular file types used to return from a download
protected enum DownloadFileType {
PLAINTEXT,
@@ -888,30 +887,46 @@
*/
protected void removeAllCurrentDownloads() {
Log.i(LOG_TAG, "Removing all current registered downloads...");
+ ArrayList<Long> ids = new ArrayList<Long>();
Cursor cursor = mDownloadManager.query(new Query());
try {
if (cursor.moveToFirst()) {
do {
int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
long downloadId = cursor.getLong(index);
-
- mDownloadManager.remove(downloadId);
+ ids.add(downloadId);
} while (cursor.moveToNext());
}
} finally {
cursor.close();
}
+ // delete all ids
+ for (long id : ids) {
+ mDownloadManager.remove(id);
+ }
+ // make sure the database is empty
+ cursor = mDownloadManager.query(new Query());
+ try {
+ assertEquals(0, cursor.getCount());
+ } finally {
+ cursor.close();
+ }
}
/**
* Helper to perform a standard enqueue of data to the mock server.
+ * download is performed to the downloads cache dir (NOT systemcache dir)
*
* @param body The body to return in the response from the server
*/
protected long doStandardEnqueue(byte[] body) throws Exception {
+ return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ }
+
+ protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
// Prepare the mock server with a standard response
enqueueResponse(HTTP_OK, body);
- return doCommonStandardEnqueue();
+ return doEnqueue(location);
}
/**
@@ -920,9 +935,13 @@
* @param body The body to return in the response from the server, contained in the file
*/
protected long doStandardEnqueue(File body) throws Exception {
+ return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ }
+
+ protected long enqueueDownloadRequest(File body, int location) throws Exception {
// Prepare the mock server with a standard response
enqueueResponse(HTTP_OK, body);
- return doCommonStandardEnqueue();
+ return doEnqueue(location);
}
/**
@@ -930,13 +949,17 @@
* doing a standard enqueue request to the server.
*/
protected long doCommonStandardEnqueue() throws Exception {
- Uri uri = getServerUri(DEFAULT_FILENAME);
- Request request = new Request(uri);
- request.setTitle(DEFAULT_FILENAME);
+ return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ }
- long dlRequest = mDownloadManager.enqueue(request);
- Log.i(LOG_TAG, "request ID: " + dlRequest);
- return dlRequest;
+ private long doEnqueue(int location) throws Exception {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
+ if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
+ request.setDestinationToSystemCache();
+ }
+
+ return mDownloadManager.enqueue(request);
}
/**
@@ -997,4 +1020,16 @@
return cursor;
}
+ /**
+ * Helper that does the actual basic download verification.
+ */
+ protected long doBasicDownload(byte[] blobData, int location) throws Exception {
+ long dlRequest = enqueueDownloadRequest(blobData, location);
+
+ // wait for the download to complete
+ waitForDownloadOrTimeout(dlRequest);
+
+ assertEquals(1, mReceiver.numDownloadsCompleted());
+ return dlRequest;
+ }
}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
similarity index 80%
rename from core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index 4f79108..06f3ba4 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -16,51 +16,35 @@
package android.app;
+import coretestutils.http.MockResponse;
+
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.app.DownloadManagerBaseTest.DataType;
-import android.app.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
-import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.StatFs;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.io.IOException;
-import java.net.URL;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
-import junit.framework.AssertionFailedError;
-
-import coretestutils.http.MockResponse;
-import coretestutils.http.MockWebServer;
-
/**
* Integration tests of the DownloadManager API.
*/
-public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
-
- private final static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
- private final static String PROHIBITED_DIRECTORY =
- Environment.getRootDirectory().getAbsolutePath();
+public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
+ private static final String TAG = "DownloadManagerFunctionalTest";
private final static String CACHE_DIR =
Environment.getDownloadCacheDirectory().getAbsolutePath();
+ private final static String PROHIBITED_DIRECTORY =
+ Environment.getRootDirectory().getAbsolutePath();
/**
* {@inheritDoc}
@@ -89,26 +73,12 @@
}
/**
- * Helper that does the actual basic download verification.
- */
- protected long doBasicDownload(byte[] blobData) throws Exception {
- long dlRequest = doStandardEnqueue(blobData);
-
- // wait for the download to complete
- waitForDownloadOrTimeout(dlRequest);
-
- assertEquals(1, mReceiver.numDownloadsCompleted());
- return dlRequest;
- }
-
- /**
* Verifies a particular error code was received from a download
*
* @param uri The uri to enqueue to the DownloadManager
* @param error The error code expected
- * @throws an Exception if the test fails
+ * @throws Exception if the test fails
*/
- @LargeTest
public void doErrorTest(Uri uri, int error) throws Exception {
Request request = new Request(uri);
request.setTitle(DEFAULT_FILENAME);
@@ -128,66 +98,57 @@
* Test a basic download of a binary file 500k in size.
*/
@LargeTest
- public void testBasicBinaryDownload() throws Exception {
- int fileSize = 500 * 1024; // 500k
+ public void testBinaryDownloadToSystemCache() throws Exception {
+ int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
- long dlRequest = doBasicDownload(blobData);
- verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+ long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ verifyDownload(dlRequest, blobData);
+ mDownloadManager.remove(dlRequest);
}
/**
* Tests the basic downloading of a text file 300000 bytes in size.
*/
@LargeTest
- public void testBasicTextDownload() throws Exception {
- int fileSize = 300000;
+ public void testTextDownloadToSystemCache() throws Exception {
+ int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.TEXT);
- long dlRequest = doBasicDownload(blobData);
- verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+ long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ verifyDownload(dlRequest, blobData);
+ mDownloadManager.remove(dlRequest);
}
-
+
/**
- * Tests when the server drops the connection after all headers (but before any data send).
+ * Helper to verify a standard single-file download from the mock server, and clean up after
+ * verification
+ *
+ * Note that this also calls the Download manager's remove, which cleans up the file from cache.
+ *
+ * @param requestId The id of the download to remove
+ * @param fileData The data to verify the file contains
*/
- @LargeTest
- public void testDropConnection_headers() throws Exception {
- byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
- MockResponse response = enqueueResponse(HTTP_OK, blobData);
- response.setCloseConnectionAfterHeader("content-length");
- long dlRequest = doCommonStandardEnqueue();
-
- // Download will never complete when header is dropped
- boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
- DEFAULT_MAX_WAIT_TIME);
-
- assertFalse(success);
- }
-
- /**
- * Tests that we get an error code when the server drops the connection during a download.
- */
- @LargeTest
- public void testServerDropConnection_body() throws Exception {
- byte[] blobData = generateData(25000, DataType.TEXT); // file size = 25000 bytes
-
- MockResponse response = enqueueResponse(HTTP_OK, blobData);
- response.setCloseConnectionAfterXBytes(15382);
- long dlRequest = doCommonStandardEnqueue();
- waitForDownloadOrTimeout(dlRequest);
-
- Cursor cursor = getCursor(dlRequest);
+ private void verifyDownload(long requestId, byte[] fileData)
+ throws Exception {
+ int fileSize = fileData.length;
+ ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
+ Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
try {
- verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_REASON,
- DownloadManager.ERROR_CANNOT_RESUME);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToFirst());
+
+ mServer.checkForExceptions();
+
+ verifyFileSize(pfd, fileSize);
+ verifyFileContents(pfd, fileData);
+ int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
+ String fileName = cursor.getString(colIndex);
+ assertTrue(fileName.startsWith(CACHE_DIR));
} finally {
+ pfd.close();
cursor.close();
}
- // Even tho the server drops the connection, we should still get a completed notification
- assertEquals(1, mReceiver.numDownloadsCompleted());
}
/**
@@ -198,7 +159,7 @@
// need to be sure all current downloads have stopped first
removeAllCurrentDownloads();
int NUM_FILES = 10;
- int MAX_FILE_SIZE = 500 * 1024; // 500 kb
+ int MAX_FILE_SIZE = 10 * 1024; // 10 kb
Random r = new LoggingRng();
for (int i=0; i<NUM_FILES; ++i) {
@@ -213,7 +174,6 @@
enqueueResponse(HTTP_OK, blobData);
long requestID = mDownloadManager.enqueue(request);
- Log.i(LOG_TAG, "request: " + i + " -- requestID: " + requestID);
}
waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
@@ -236,8 +196,6 @@
assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
} finally {
- Log.i(LOG_TAG, "All download IDs: " + mReceiver.getDownloadIds().toString());
- Log.i(LOG_TAG, "Total downloads completed: " + mReceiver.getDownloadIds().size());
cursor.close();
}
}
@@ -258,7 +216,6 @@
Request request = new Request(uri);
Uri localUri = Uri.fromFile(existentFile);
- Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
request.setDestinationUri(localUri);
long dlRequest = mDownloadManager.enqueue(request);
@@ -268,9 +225,7 @@
Cursor cursor = getCursor(dlRequest);
try {
- verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_REASON,
- DownloadManager.ERROR_FILE_ALREADY_EXISTS);
+ verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_SUCCESSFUL);
} finally {
cursor.close();
}
@@ -299,7 +254,6 @@
Request request = new Request(uri);
Uri localUri = Uri.fromFile(downloadedFile);
- Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
request.setDestinationUri(localUri);
long dlRequest = mDownloadManager.enqueue(request);
@@ -331,7 +285,6 @@
Request request = new Request(uri);
Uri localUri = Uri.fromFile(downloadedFile);
- Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
request.setDestinationUri(localUri);
try {
@@ -347,144 +300,6 @@
}
/**
- * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
- * once Wifi is re-enabled.
- */
- @LargeTest
- public void testDownloadNoWifi() throws Exception {
- long timeout = 60 * 1000; // wait only 60 seconds before giving up
- int fileSize = 140 * 1024; // 140k
- byte[] blobData = generateData(fileSize, DataType.TEXT);
-
- setWiFiStateOn(false);
- enqueueResponse(HTTP_OK, blobData);
-
- try {
- Uri uri = getServerUri(DEFAULT_FILENAME);
- Request request = new Request(uri);
- request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
-
- long dlRequest = mDownloadManager.enqueue(request);
-
- // wait for the download to complete
- boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
- WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
- assertFalse("Download proceeded without Wifi connection!", success);
-
- setWiFiStateOn(true);
- waitForDownloadOrTimeout(dlRequest);
-
- assertEquals(1, mReceiver.numDownloadsCompleted());
- } finally {
- setWiFiStateOn(true);
- }
- }
-
- /**
- * Tests downloading a file to cache when there isn't enough space in the cache to hold the
- * entire file.
- */
- @LargeTest
- public void testDownloadToCache_whenFull() throws Exception {
- int DOWNLOAD_FILE_SIZE = 500000;
-
- StatFs fs = new StatFs(CACHE_DIR);
- Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
- Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
-
- int blockSize = fs.getBlockSize();
- int availableBlocks = fs.getAvailableBlocks();
- int availableBytes = blockSize * availableBlocks;
- File outFile = null;
-
- try {
- // fill cache to ensure we don't have enough space - take half the size of the
- // download size, and leave that much freespace left on the cache partition
- if (DOWNLOAD_FILE_SIZE <= availableBytes) {
- int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
-
- int writeSizeBlocks = writeSizeBytes / blockSize;
- int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
-
- FileOutputStream fo = null;
- try {
- outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
- Log.v(LOG_TAG, "writing " + writeSizeBlocks + " blocks to file "
- + outFile.getAbsolutePath());
-
- fo = new FileOutputStream(outFile);
-
- byte[] buffer = new byte[blockSize];
- while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
- fo.write(buffer);
- fs.restat(CACHE_DIR);
- }
- } catch (IOException e) {
- Log.e(LOG_TAG, "error filling file: ", e);
- throw e;
- } finally {
- if (fo != null) {
- fo.close();
- }
- }
- }
-
- Log.i(LOG_TAG, "Done creating filler file.");
- assertTrue(DOWNLOAD_FILE_SIZE > (fs.getAvailableBlocks() * blockSize));
- byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
- long dlRequest = doBasicDownload(blobData);
- verifyAndCleanupSingleFileDownload(dlRequest, blobData);
-
- } finally {
- if (outFile != null) {
- outFile.delete();
- }
- }
- }
-
- /**
- * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
- * run out of space.
- */
- @LargeTest
- public void testDownloadCacheNonPurgeable() throws Exception {
- int fileSize = 10000000;
- byte[] blobData = generateData(fileSize, DataType.BINARY);
- long dlRequest = -1;
-
- // Fill up the cache partition until there's not enough room for another download.
- // Note that we need to initiate a download first, then check for the available space. This
- // is b/c there could be some files that are still left in the cache that can (and will be)
- // cleared out, but not until DM gets a request for a download and reclaims that
- // space first.
- boolean spaceAvailable = true;
- while (spaceAvailable) {
- dlRequest = doStandardEnqueue(blobData);
- waitForDownloadOrTimeout(dlRequest);
-
- // Check if we've filled up the cache yet
- StatFs fs = new StatFs(CACHE_DIR);
- Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
- Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
- int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
- spaceAvailable = (availableBytes > fileSize) ? true : false;
- }
-
- // Now add one more download (should not fit in the space left over)
- dlRequest = doStandardEnqueue(blobData);
- waitForDownloadOrTimeout(dlRequest);
-
- // For the last download we should have failed b/c there is not enough space left in cache
- Cursor cursor = getCursor(dlRequest);
- try {
- verifyInt(cursor, DownloadManager.COLUMN_REASON,
- DownloadManager.ERROR_INSUFFICIENT_SPACE);
- } finally {
- cursor.close();
- }
- }
-
- /**
* Tests that we get the correct download ID from the download notification.
*/
@LargeTest
@@ -545,10 +360,10 @@
*/
@LargeTest
public void testRemoveDownload() throws Exception {
- int fileSize = 100 * 1024; // 100k
+ int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
- long dlRequest = doBasicDownload(blobData);
+ long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
try {
assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
@@ -565,7 +380,7 @@
*/
@LargeTest
public void testSetTitle() throws Exception {
- int fileSize = 50 * 1024; // 50k
+ int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
MockResponse response = enqueueResponse(HTTP_OK, blobData);
@@ -587,4 +402,179 @@
cursor.close();
}
}
+
+ /**
+ * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
+ * once Wifi is re-enabled.
+ */
+ @LargeTest
+ public void testDownloadNoWifi() throws Exception {
+ long timeout = 60 * 1000; // wait only 60 seconds before giving up
+ int fileSize = 1024; // 140k
+ byte[] blobData = generateData(fileSize, DataType.TEXT);
+
+ setWiFiStateOn(false);
+ enqueueResponse(HTTP_OK, blobData);
+
+ try {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ Request request = new Request(uri);
+ request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
+
+ long dlRequest = mDownloadManager.enqueue(request);
+
+ // wait for the download to complete
+ boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
+ WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
+ assertFalse("Download proceeded without Wifi connection!", success);
+
+ setWiFiStateOn(true);
+ waitForDownloadOrTimeout(dlRequest);
+
+ assertEquals(1, mReceiver.numDownloadsCompleted());
+ } finally {
+ setWiFiStateOn(true);
+ }
+ }
+
+ /**
+ * Tests when the server drops the connection after all headers (but before any data send).
+ */
+ @LargeTest
+ public void testDropConnection_headers() throws Exception {
+ byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+ MockResponse response = enqueueResponse(HTTP_OK, blobData);
+ response.setCloseConnectionAfterHeader("content-length");
+ long dlRequest = doCommonStandardEnqueue();
+
+ // Download will never complete when header is dropped
+ boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
+ DEFAULT_MAX_WAIT_TIME);
+
+ assertFalse(success);
+ }
+
+ /**
+ * Tests that we get an error code when the server drops the connection during a download.
+ */
+ @LargeTest
+ public void testServerDropConnection_body() throws Exception {
+ byte[] blobData = generateData(25000, DataType.TEXT); // file size = 25000 bytes
+
+ MockResponse response = enqueueResponse(HTTP_OK, blobData);
+ response.setCloseConnectionAfterXBytes(15382);
+ long dlRequest = doCommonStandardEnqueue();
+ waitForDownloadOrTimeout(dlRequest);
+
+ Cursor cursor = getCursor(dlRequest);
+ try {
+ verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
+ DownloadManager.ERROR_CANNOT_RESUME);
+ } finally {
+ cursor.close();
+ }
+ // Even tho the server drops the connection, we should still get a completed notification
+ assertEquals(1, mReceiver.numDownloadsCompleted());
+ }
+
+ /**
+ * Tests downloading a file to system cache when there isn't enough space in the system cache
+ * to hold the entire file. DownloadManager deletes enough files to make space for the
+ * new download.
+ */
+ @LargeTest
+ public void testDownloadToCacheWithAlmostFullCache() throws Exception {
+ int DOWNLOAD_FILE_SIZE = 1024 * 1024; // 1MB
+
+ StatFs fs = new StatFs(CACHE_DIR);
+ int blockSize = fs.getBlockSize();
+ int availableBlocks = fs.getAvailableBlocks();
+ int availableBytes = blockSize * availableBlocks;
+ Log.i(TAG, "INITIAL stage, available space in /cache: " + availableBytes);
+ File outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
+ byte[] buffer = new byte[blockSize];
+
+ try {
+ // fill cache to ensure we don't have enough space - take half the size of the
+ // download size, and leave that much freespace left on the cache partition
+ if (DOWNLOAD_FILE_SIZE <= availableBytes) {
+ int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
+
+ int writeSizeBlocks = writeSizeBytes / blockSize;
+ int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
+
+ FileOutputStream fo = null;
+ try {
+ fo = new FileOutputStream(outFile);
+ while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
+ fo.write(buffer);
+ fs.restat(CACHE_DIR);
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error filling file: ", e);
+ throw e;
+ } finally {
+ if (fo != null) {
+ fo.close();
+ }
+ }
+ }
+
+ // /cache should now be almost full.
+ long spaceAvailable = fs.getAvailableBlocks() * blockSize;
+ Log.i(TAG, "BEFORE download, available space in /cache: " + spaceAvailable);
+ assertTrue(DOWNLOAD_FILE_SIZE > spaceAvailable);
+
+ // try to download 1MB file into /cache - and it should succeed
+ byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
+ long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+ } finally {
+ if (outFile != null) {
+ outFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
+ * run out of space.
+ */
+ @LargeTest
+ public void testDownloadToCacheNonPurgeableWithFullCache() throws Exception {
+ int fileSize = 1024 * 1024; // 1MB
+ byte[] blobData = generateData(fileSize, DataType.BINARY);
+ long dlRequest = -1;
+
+ // Fill up the cache partition with DOWNLOAD_CACHE_NON_PURGEABLE downloads
+ // until 500KB is left.
+ boolean spaceAvailable = true;
+ while (spaceAvailable) {
+ // since this tests package has android.permission.DOWNLOAD_CACHE_NON_PURGEABLE
+ // permission, downloads are automatically set to be DOWNLOAD_CACHE_NON_PURGEABLE
+ dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ waitForDownloadOrTimeout(dlRequest);
+
+ // Check if we've filled up the cache yet
+ StatFs fs = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+ int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
+ Log.i(TAG, "available space in /cache: " + availableBytes);
+ spaceAvailable = (availableBytes > fileSize) ? true : false;
+ }
+
+ // Now add one more 1MB download (should not fit in the space left over)
+ dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ waitForDownloadOrTimeout(dlRequest);
+
+ // For the last download we should have failed b/c there is not enough space left in cache
+ Cursor cursor = getCursor(dlRequest);
+ try {
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
+ DownloadManager.ERROR_INSUFFICIENT_SPACE);
+ } finally {
+ cursor.close();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index e0b28d0..18b279f 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -16,19 +16,21 @@
package android.app;
-import java.io.File;
-import java.util.Random;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
+import java.io.File;
+import java.util.Random;
+
public class DownloadManagerStressTest extends DownloadManagerBaseTest {
- private static String LOG_TAG = "android.net.DownloadManagerStressTest";
/**
* {@inheritDoc}
@@ -42,8 +44,25 @@
}
/**
- * Attempts to downloading thousands of files simultaneously
+ * {@inheritDoc}
*/
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ setWiFiStateOn(true);
+ removeAllCurrentDownloads();
+
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ /**
+ * Attempts to downloading thousands of files simultaneously
+ * don't run this test - downloadmanager needs to allow only a few simultaneous downloads.
+ */
+ @Suppress
public void testDownloadThousands() throws Exception {
int NUM_FILES = 1500;
int MAX_FILE_SIZE = 3000;
@@ -106,6 +125,7 @@
/**
* Tests trying to download a large file (50M bytes).
*/
+ @LargeTest
public void testDownloadLargeFile() throws Exception {
long fileSize = 50000000L; // note: kept relatively small to not exceed /cache dir size
File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
@@ -129,31 +149,4 @@
largeFile.delete();
}
}
-
- /**
- * Tests trying to download a large file (~600M bytes) when there's not enough space in cache
- */
- public void testInsufficientSpace() throws Exception {
- // @TODO: Rework this to fill up cache partition with a dynamically calculated size
- long fileSize = 600000000L;
- File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
-
- Cursor cursor = null;
- try {
- long dlRequest = doStandardEnqueue(largeFile);
-
- // wait for the download to complete
- waitForDownloadOrTimeout(dlRequest);
-
- cursor = getCursor(dlRequest);
- verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_REASON,
- DownloadManager.ERROR_INSUFFICIENT_SPACE);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- largeFile.delete();
- }
- }
}
diff --git a/core/tests/coretests/src/android/net/http/ProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
similarity index 91%
rename from core/tests/coretests/src/android/net/http/ProxyTest.java
rename to core/tests/coretests/src/android/net/http/AbstractProxyTest.java
index 8175531..2fb833c 100644
--- a/core/tests/coretests/src/android/net/http/ProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
@@ -33,15 +33,16 @@
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.impl.client.DefaultHttpClient;
import tests.http.MockResponse;
import tests.http.MockWebServer;
import tests.http.RecordedRequest;
-public class ProxyTest extends TestCase {
+public abstract class AbstractProxyTest extends TestCase {
private MockWebServer server = new MockWebServer();
+ protected abstract HttpClient newHttpClient();
+
@Override protected void tearDown() throws Exception {
System.clearProperty("proxyHost");
System.clearProperty("proxyPort");
@@ -54,7 +55,7 @@
super.tearDown();
}
- public void testConnectToHttps() throws IOException, InterruptedException {
+ public void testConnectToHttps() throws Exception {
TestSSLContext testSSLContext = TestSSLContext.create();
server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
@@ -63,9 +64,9 @@
.setBody("this response comes via HTTPS"));
server.play();
- HttpClient httpClient = new DefaultHttpClient();
- SSLSocketFactory sslSocketFactory = new SSLSocketFactory(
- testSSLContext.clientContext.getSocketFactory());
+ HttpClient httpClient = newHttpClient();
+
+ SSLSocketFactory sslSocketFactory = newSslSocketFactory(testSSLContext);
sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier());
httpClient.getConnectionManager().getSchemeRegistry()
.register(new Scheme("https", sslSocketFactory, server.getPort()));
@@ -78,6 +79,12 @@
assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
}
+ private SSLSocketFactory newSslSocketFactory(TestSSLContext testSSLContext) throws Exception {
+ // call through to Apache HTTP's non-public SSLSocketFactory constructor
+ return SSLSocketFactory.class.getConstructor(javax.net.ssl.SSLSocketFactory.class)
+ .newInstance(testSSLContext.clientContext.getSocketFactory());
+ }
+
/**
* We had bugs where proxy system properties weren't being honored.
* http://b/3254717
@@ -108,7 +115,7 @@
server.enqueue(mockResponse);
server.play();
- HttpClient httpProxyClient = new DefaultHttpClient();
+ HttpClient httpProxyClient = newHttpClient();
HttpGet request = new HttpGet("http://android.com/foo");
proxyConfig.configure(server, httpProxyClient, request);
@@ -150,9 +157,8 @@
.setBody("this response comes via a secure proxy"));
server.play();
- HttpClient httpProxyClient = new DefaultHttpClient();
- SSLSocketFactory sslSocketFactory = new SSLSocketFactory(
- testSSLContext.clientContext.getSocketFactory());
+ HttpClient httpProxyClient = newHttpClient();
+ SSLSocketFactory sslSocketFactory = newSslSocketFactory(testSSLContext);
sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier());
httpProxyClient.getConnectionManager().getSchemeRegistry()
.register(new Scheme("https", sslSocketFactory, 443));
@@ -187,7 +193,7 @@
System.setProperty("http.proxyHost", "proxy.foo");
System.setProperty("http.proxyPort", "8080");
- HttpClient client = new DefaultHttpClient();
+ HttpClient client = newHttpClient();
HttpGet request = new HttpGet("http://origin.foo/bar");
proxyConfig.configure(server, client, request);
HttpResponse response = client.execute(request);
@@ -203,7 +209,7 @@
System.setProperty("http.proxyHost", "proxy.foo");
System.setProperty("http.proxyPort", "8080");
- HttpClient client = new DefaultHttpClient();
+ HttpClient client = newHttpClient();
HttpGet request = new HttpGet(server.getUrl("/bar").toURI());
request.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, ConnRouteParams.NO_HOST);
HttpResponse response = client.execute(request);
diff --git a/core/tests/coretests/src/android/net/http/AndroidHttpClientProxyTest.java b/core/tests/coretests/src/android/net/http/AndroidHttpClientProxyTest.java
new file mode 100644
index 0000000..b33bdb8
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/AndroidHttpClientProxyTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import org.apache.http.client.HttpClient;
+
+public final class AndroidHttpClientProxyTest extends AbstractProxyTest {
+
+ @Override protected HttpClient newHttpClient() {
+ return AndroidHttpClient.newInstance(AbstractProxyTest.class.getName());
+ }
+}
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientProxyTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientProxyTest.java
new file mode 100644
index 0000000..d6313ec
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientProxyTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+public final class DefaultHttpClientProxyTest extends AbstractProxyTest {
+
+ @Override protected HttpClient newHttpClient() {
+ return new DefaultHttpClient();
+ }
+}
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
new file mode 100644
index 0000000..ad3ec3d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import junit.framework.TestCase;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+import tests.http.SocketPolicy;
+import static tests.http.SocketPolicy.DISCONNECT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
+
+public final class DefaultHttpClientTest extends TestCase {
+
+ private MockWebServer server = new MockWebServer();
+
+ @Override protected void tearDown() throws Exception {
+ server.shutdown();
+ super.tearDown();
+ }
+
+ public void testServerClosesSocket() throws Exception {
+ testServerClosesOutput(DISCONNECT_AT_END);
+ }
+
+ public void testServerShutdownInput() throws Exception {
+ testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
+ }
+
+ /**
+ * DefaultHttpClient fails if the server shutdown the output after the
+ * response was sent. http://b/2612240
+ */
+ public void testServerShutdownOutput() throws Exception {
+ testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
+ }
+
+ private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
+ server.enqueue(new MockResponse()
+ .setBody("This connection won't pool properly")
+ .setSocketPolicy(socketPolicy));
+ server.enqueue(new MockResponse()
+ .setBody("This comes after a busted connection"));
+ server.play();
+
+ DefaultHttpClient client = new DefaultHttpClient();
+
+ HttpResponse a = client.execute(new HttpGet(server.getUrl("/a").toURI()));
+ assertEquals("This connection won't pool properly", contentToString(a));
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+
+ HttpResponse b = client.execute(new HttpGet(server.getUrl("/b").toURI()));
+ assertEquals("This comes after a busted connection", contentToString(b));
+ // sequence number 0 means the HTTP socket connection was not reused
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+ }
+
+ private String contentToString(HttpResponse response) throws IOException {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new InputStreamReader(response.getEntity().getContent());
+ int length;
+ while ((length = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, length);
+ }
+ reader.close();
+ return writer.toString();
+ }
+}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 7206f4a..a419068 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -18,8 +18,7 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- ../../../coretests/src/android/app/DownloadManagerBaseTest.java
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
LOCAL_SDK_VERSION := current
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java
new file mode 100644
index 0000000..acd2a18
--- /dev/null
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java
@@ -0,0 +1,816 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.downloadmanagertests;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.provider.Settings;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.TimeoutException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+
+import coretestutils.http.MockResponse;
+import coretestutils.http.MockWebServer;
+
+/**
+ * Base class for Instrumented tests for the Download Manager.
+ */
+public class DownloadManagerBaseTest extends InstrumentationTestCase {
+
+ protected DownloadManager mDownloadManager = null;
+ protected MockWebServer mServer = null;
+ protected String mFileType = "text/plain";
+ protected Context mContext = null;
+ protected MultipleDownloadsCompletedReceiver mReceiver = null;
+ protected static final int DEFAULT_FILE_SIZE = 10 * 1024; // 10kb
+ protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
+
+ protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
+ protected static final int HTTP_OK = 200;
+ protected static final int HTTP_REDIRECT = 307;
+ protected static final int HTTP_PARTIAL_CONTENT = 206;
+ protected static final int HTTP_NOT_FOUND = 404;
+ protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
+ protected String DEFAULT_FILENAME = "somefile.txt";
+
+ protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000; // 2 minutes
+ protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds
+
+ protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
+ protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
+ protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
+
+ protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
+ protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
+
+ // Just a few popular file types used to return from a download
+ protected enum DownloadFileType {
+ PLAINTEXT,
+ APK,
+ GIF,
+ GARBAGE,
+ UNRECOGNIZED,
+ ZIP
+ }
+
+ protected enum DataType {
+ TEXT,
+ BINARY
+ }
+
+ public static class LoggingRng extends Random {
+
+ /**
+ * Constructor
+ *
+ * Creates RNG with self-generated seed value.
+ */
+ public LoggingRng() {
+ this(SystemClock.uptimeMillis());
+ }
+
+ /**
+ * Constructor
+ *
+ * Creats RNG with given initial seed value
+
+ * @param seed The initial seed value
+ */
+ public LoggingRng(long seed) {
+ super(seed);
+ Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
+ }
+ }
+
+ public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
+ private volatile int mNumDownloadsCompleted = 0;
+ private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
+ synchronized(this) {
+ long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
+ Log.i(LOG_TAG, "Received Notification for download: " + id);
+ if (!downloadIds.contains(id)) {
+ ++mNumDownloadsCompleted;
+ Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
+ intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
+ downloadIds.add(id);
+
+ DownloadManager dm = (DownloadManager)context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+
+ Cursor cursor = dm.query(new Query().setFilterById(id));
+ try {
+ if (cursor.moveToFirst()) {
+ int status = cursor.getInt(cursor.getColumnIndex(
+ DownloadManager.COLUMN_STATUS));
+ Log.i(LOG_TAG, "Download status is: " + status);
+ } else {
+ fail("No status found for completed download!");
+ }
+ } finally {
+ cursor.close();
+ }
+ } else {
+ Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the number of times the {@link #onReceive} callback has been called for the
+ * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
+ * downloads completed thus far.
+ *
+ * @return the number of downloads completed so far.
+ */
+ public int numDownloadsCompleted() {
+ return mNumDownloadsCompleted;
+ }
+
+ /**
+ * Gets the list of download IDs.
+ * @return A Set<Long> with the ids of the completed downloads.
+ */
+ public Set<Long> getDownloadIds() {
+ synchronized(this) {
+ Set<Long> returnIds = new HashSet<Long>(downloadIds);
+ return returnIds;
+ }
+ }
+
+ }
+
+ public static class WiFiChangedReceiver extends BroadcastReceiver {
+ private Context mContext = null;
+
+ /**
+ * Constructor
+ *
+ * Sets the current state of WiFi.
+ *
+ * @param context The current app {@link Context}.
+ */
+ public WiFiChangedReceiver(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
+ synchronized (this) {
+ this.notify();
+ }
+ }
+ }
+
+ /**
+ * Gets the current state of WiFi.
+ *
+ * @return Returns true if WiFi is on, false otherwise.
+ */
+ public boolean getWiFiIsOn() {
+ ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
+ return info.isConnected();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ mContext = getInstrumentation().getContext();
+ mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ mServer = new MockWebServer();
+ mReceiver = registerNewMultipleDownloadsReceiver();
+ // Note: callers overriding this should call mServer.play() with the desired port #
+ }
+
+ /**
+ * Helper to enqueue a response from the MockWebServer.
+ *
+ * @param status The HTTP status code to return for this response
+ * @param body The body to return in this response
+ * @return Returns the mock web server response that was queued (which can be modified)
+ */
+ private MockResponse enqueueResponse(int status, byte[] body) {
+ return doEnqueueResponse(status).setBody(body);
+
+ }
+
+ /**
+ * Helper to enqueue a response from the MockWebServer.
+ *
+ * @param status The HTTP status code to return for this response
+ * @param bodyFile The body to return in this response
+ * @return Returns the mock web server response that was queued (which can be modified)
+ */
+ private MockResponse enqueueResponse(int status, File bodyFile) {
+ return doEnqueueResponse(status).setBody(bodyFile);
+ }
+
+ /**
+ * Helper for enqueue'ing a response from the MockWebServer.
+ *
+ * @param status The HTTP status code to return for this response
+ * @return Returns the mock web server response that was queued (which can be modified)
+ */
+ private MockResponse doEnqueueResponse(int status) {
+ MockResponse response = new MockResponse().setResponseCode(status);
+ response.addHeader("Content-type", mFileType);
+ mServer.enqueue(response);
+ return response;
+ }
+
+ /**
+ * Helper to generate a random blob of bytes using a given RNG.
+ *
+ * @param size The size of the data to generate
+ * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
+ * {@link DataType.BINARY}.
+ * @param rng (optional) The RNG to use; pass null to use
+ * @return The random data that is generated.
+ */
+ private byte[] generateData(int size, DataType type, Random rng) {
+ int min = Byte.MIN_VALUE;
+ int max = Byte.MAX_VALUE;
+
+ // Only use chars in the HTTP ASCII printable character range for Text
+ if (type == DataType.TEXT) {
+ min = 32;
+ max = 126;
+ }
+ byte[] result = new byte[size];
+ Log.i(LOG_TAG, "Generating data of size: " + size);
+
+ if (rng == null) {
+ rng = new LoggingRng();
+ }
+
+ for (int i = 0; i < size; ++i) {
+ result[i] = (byte) (min + rng.nextInt(max - min + 1));
+ }
+ return result;
+ }
+
+ /**
+ * Helper to verify the size of a file.
+ *
+ * @param pfd The input file to compare the size of
+ * @param size The expected size of the file
+ */
+ protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
+ assertEquals(pfd.getStatSize(), size);
+ }
+
+ /**
+ * Helper to verify the contents of a downloaded file versus a byte[].
+ *
+ * @param actual The file of whose contents to verify
+ * @param expected The data we expect to find in the aforementioned file
+ * @throws IOException if there was a problem reading from the file
+ */
+ private void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
+ throws IOException {
+ AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
+ long fileSize = actual.getStatSize();
+
+ assertTrue(fileSize <= Integer.MAX_VALUE);
+ assertEquals(expected.length, fileSize);
+
+ byte[] actualData = new byte[expected.length];
+ assertEquals(input.read(actualData), fileSize);
+ compareByteArrays(actualData, expected);
+ }
+
+ /**
+ * Helper to compare 2 byte arrays.
+ *
+ * @param actual The array whose data we want to verify
+ * @param expected The array of data we expect to see
+ */
+ private void compareByteArrays(byte[] actual, byte[] expected) {
+ assertEquals(actual.length, expected.length);
+ int length = actual.length;
+ for (int i = 0; i < length; ++i) {
+ // assert has a bit of overhead, so only do the assert when the values are not the same
+ if (actual[i] != expected[i]) {
+ fail("Byte arrays are not equal.");
+ }
+ }
+ }
+
+ /**
+ * Gets the MIME content string for a given type
+ *
+ * @param type The MIME type to return
+ * @return the String representation of that MIME content type
+ */
+ protected String getMimeMapping(DownloadFileType type) {
+ switch (type) {
+ case APK:
+ return "application/vnd.android.package-archive";
+ case GIF:
+ return "image/gif";
+ case ZIP:
+ return "application/x-zip-compressed";
+ case GARBAGE:
+ return "zip\\pidy/doo/da";
+ case UNRECOGNIZED:
+ return "application/new.undefined.type.of.app";
+ }
+ return "text/plain";
+ }
+
+ /**
+ * Gets the Uri that should be used to access the mock server
+ *
+ * @param filename The name of the file to try to retrieve from the mock server
+ * @return the Uri to use for access the file on the mock server
+ */
+ private Uri getServerUri(String filename) throws Exception {
+ URL url = mServer.getUrl("/" + filename);
+ return Uri.parse(url.toString());
+ }
+
+ /**
+ * Helper to create and register a new MultipleDownloadCompletedReciever
+ *
+ * This is used to track many simultaneous downloads by keeping count of all the downloads
+ * that have completed.
+ *
+ * @return A new receiver that records and can be queried on how many downloads have completed.
+ */
+ protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
+ MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
+ mContext.registerReceiver(receiver, new IntentFilter(
+ DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+ return receiver;
+ }
+
+ /**
+ * Enables or disables WiFi.
+ *
+ * Note: Needs the following permissions:
+ * android.permission.ACCESS_WIFI_STATE
+ * android.permission.CHANGE_WIFI_STATE
+ * @param enable true if it should be enabled, false if it should be disabled
+ */
+ protected void setWiFiStateOn(boolean enable) throws Exception {
+ Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
+ WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
+
+ manager.setWifiEnabled(enable);
+
+ String timeoutMessage = "Timed out waiting for Wifi to be "
+ + (enable ? "enabled!" : "disabled!");
+
+ WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
+ mContext.registerReceiver(receiver, new IntentFilter(
+ ConnectivityManager.CONNECTIVITY_ACTION));
+
+ synchronized (receiver) {
+ long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
+ boolean timedOut = false;
+
+ while (receiver.getWiFiIsOn() != enable && !timedOut) {
+ try {
+ receiver.wait(DEFAULT_WAIT_POLL_TIME);
+
+ if (SystemClock.elapsedRealtime() > timeoutTime) {
+ timedOut = true;
+ }
+ }
+ catch (InterruptedException e) {
+ // ignore InterruptedExceptions
+ }
+ }
+ if (timedOut) {
+ fail(timeoutMessage);
+ }
+ }
+ assertEquals(enable, receiver.getWiFiIsOn());
+ }
+
+ /**
+ * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
+ * indicating that the mode has changed.
+ *
+ * Note: Needs the following permission:
+ * android.permission.WRITE_SETTINGS
+ * @param enable true if airplane mode should be ON, false if it should be OFF
+ */
+ protected void setAirplaneModeOn(boolean enable) throws Exception {
+ int state = enable ? 1 : 0;
+
+ // Change the system setting
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+ state);
+
+ String timeoutMessage = "Timed out waiting for airplane mode to be " +
+ (enable ? "enabled!" : "disabled!");
+
+ // wait for airplane mode to change state
+ int currentWaitTime = 0;
+ while (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON, -1) != state) {
+ timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
+ timeoutMessage);
+ }
+
+ // Post the intent
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", true);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Helper to wait for a particular download to finish, or else a timeout to occur
+ *
+ * Does not wait for a receiver notification of the download.
+ *
+ * @param id The download id to query on (wait for)
+ */
+ private void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
+ InterruptedException {
+ waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
+ }
+
+ /**
+ * Helper to wait for a particular download to finish, or else a timeout to occur
+ *
+ * Also guarantees a notification has been posted for the download.
+ *
+ * @param id The download id to query on (wait for)
+ */
+ protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
+ InterruptedException {
+ waitForDownloadOrTimeout_skipNotification(id);
+ waitForReceiverNotifications(1);
+ }
+
+ /**
+ * Helper to wait for a particular download to finish, or else a timeout to occur
+ *
+ * Also guarantees a notification has been posted for the download.
+ *
+ * @param id The download id to query on (wait for)
+ * @param poll The amount of time to wait
+ * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+ */
+ protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
+ throws TimeoutException, InterruptedException {
+ doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
+ waitForReceiverNotifications(1);
+ }
+
+ /**
+ * Helper to wait for all downloads to finish, or else a specified timeout to occur
+ *
+ * Makes no guaranee that notifications have been posted for all downloads.
+ *
+ * @param poll The amount of time to wait
+ * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+ */
+ protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
+ InterruptedException {
+ doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
+ }
+
+ /**
+ * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
+ *
+ * Also guarantees a notification has been posted for the download.
+ *
+ * @param id The id of the download to query against
+ * @param poll The amount of time to wait
+ * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+ * @return true if download completed successfully (didn't timeout), false otherwise
+ */
+ private boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
+ try {
+ doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
+ waitForReceiverNotifications(1);
+ } catch (TimeoutException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
+ *
+ * @param currentTotalWaitTime The total time waited so far
+ * @param poll The amount of time to wait
+ * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
+ * we timeout and fail
+ * @param timedOutMessage The message to display in the failure message if we timeout
+ * @return The new total amount of time we've waited so far
+ * @throws TimeoutException if timed out waiting for SD card to mount
+ */
+ private int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
+ String timedOutMessage) throws TimeoutException {
+ long now = SystemClock.elapsedRealtime();
+ long end = now + poll;
+
+ // if we get InterruptedException's, ignore them and just keep sleeping
+ while (now < end) {
+ try {
+ Thread.sleep(end - now);
+ } catch (InterruptedException e) {
+ // ignore interrupted exceptions
+ }
+ now = SystemClock.elapsedRealtime();
+ }
+
+ currentTotalWaitTime += poll;
+ if (currentTotalWaitTime > maxTimeoutMillis) {
+ throw new TimeoutException(timedOutMessage);
+ }
+ return currentTotalWaitTime;
+ }
+
+ /**
+ * Helper to wait for all downloads to finish, or else a timeout to occur
+ *
+ * @param query The query to pass to the download manager
+ * @param poll The poll time to wait between checks
+ * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
+ */
+ private void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
+ throws TimeoutException {
+ int currentWaitTime = 0;
+ while (true) {
+ query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
+ | DownloadManager.STATUS_RUNNING);
+ Cursor cursor = mDownloadManager.query(query);
+
+ try {
+ if (cursor.getCount() == 0) {
+ Log.i(LOG_TAG, "All downloads should be done...");
+ break;
+ }
+ currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
+ "Timed out waiting for all downloads to finish");
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * Synchronously waits for external store to be mounted (eg: SD Card).
+ *
+ * @throws InterruptedException if interrupted
+ * @throws Exception if timed out waiting for SD card to mount
+ */
+ protected void waitForExternalStoreMount() throws Exception {
+ String extStorageState = Environment.getExternalStorageState();
+ int currentWaitTime = 0;
+ while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
+ Log.i(LOG_TAG, "Waiting for SD card...");
+ currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
+ DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
+ extStorageState = Environment.getExternalStorageState();
+ }
+ }
+
+ /**
+ * Synchronously waits for a download to start.
+ *
+ * @param dlRequest the download request id used by Download Manager to track the download.
+ * @throws Exception if timed out while waiting for SD card to mount
+ */
+ protected void waitForDownloadToStart(long dlRequest) throws Exception {
+ Cursor cursor = getCursor(dlRequest);
+ try {
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ int value = cursor.getInt(columnIndex);
+ int currentWaitTime = 0;
+
+ while (value != DownloadManager.STATUS_RUNNING &&
+ (value != DownloadManager.STATUS_FAILED) &&
+ (value != DownloadManager.STATUS_SUCCESSFUL)) {
+ Log.i(LOG_TAG, "Waiting for download to start...");
+ currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+ MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
+ cursor.requery();
+ assertTrue(cursor.moveToFirst());
+ columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ value = cursor.getInt(columnIndex);
+ }
+ assertFalse("Download failed immediately after start",
+ value == DownloadManager.STATUS_FAILED);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Synchronously waits for our receiver to receive notification for a given number of
+ * downloads.
+ *
+ * @param targetNumber The number of notifications for unique downloads to wait for; pass in
+ * -1 to not wait for notification.
+ * @throws Exception if timed out while waiting
+ */
+ private void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
+ int count = mReceiver.numDownloadsCompleted();
+ int currentWaitTime = 0;
+
+ while (count < targetNumber) {
+ Log.i(LOG_TAG, "Waiting for notification of downloads...");
+ currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+ MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
+ + " Received " + count + "notifications.");
+ count = mReceiver.numDownloadsCompleted();
+ }
+ }
+
+ /**
+ * Synchronously waits for a file to increase in size (such as to monitor that a download is
+ * progressing).
+ *
+ * @param file The file whose size to track.
+ * @throws Exception if timed out while waiting for the file to grow in size.
+ */
+ protected void waitForFileToGrow(File file) throws Exception {
+ int currentWaitTime = 0;
+
+ // File may not even exist yet, so wait until it does (or we timeout)
+ while (!file.exists()) {
+ Log.i(LOG_TAG, "Waiting for file to exist...");
+ currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+ MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
+ }
+
+ // Get original file size...
+ long originalSize = file.length();
+
+ while (file.length() <= originalSize) {
+ Log.i(LOG_TAG, "Waiting for file to be written to...");
+ currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+ MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
+ }
+ }
+
+ /**
+ * Helper to remove all downloads that are registered with the DL Manager.
+ *
+ * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
+ * paused, or have completed.
+ */
+ protected void removeAllCurrentDownloads() {
+ Log.i(LOG_TAG, "Removing all current registered downloads...");
+ Cursor cursor = mDownloadManager.query(new Query());
+ try {
+ if (cursor.moveToFirst()) {
+ do {
+ int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+ long downloadId = cursor.getLong(index);
+
+ mDownloadManager.remove(downloadId);
+ } while (cursor.moveToNext());
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Helper to perform a standard enqueue of data to the mock server.
+ * download is performed to the downloads cache dir (NOT systemcache dir)
+ *
+ * @param body The body to return in the response from the server
+ */
+ private long doStandardEnqueue(byte[] body) throws Exception {
+ // Prepare the mock server with a standard response
+ enqueueResponse(HTTP_OK, body);
+ return doCommonStandardEnqueue();
+ }
+
+ /**
+ * Helper to perform a standard enqueue of data to the mock server.
+ *
+ * @param body The body to return in the response from the server, contained in the file
+ */
+ private long doStandardEnqueue(File body) throws Exception {
+ // Prepare the mock server with a standard response
+ enqueueResponse(HTTP_OK, body);
+ return doCommonStandardEnqueue();
+ }
+
+ /**
+ * Helper to do the additional steps (setting title and Uri of default filename) when
+ * doing a standard enqueue request to the server.
+ */
+ private long doCommonStandardEnqueue() throws Exception {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
+ return mDownloadManager.enqueue(request);
+ }
+
+ /**
+ * Helper to verify an int value in a Cursor
+ *
+ * @param cursor The cursor containing the query results
+ * @param columnName The name of the column to query
+ * @param expected The expected int value
+ */
+ private void verifyInt(Cursor cursor, String columnName, int expected) {
+ int index = cursor.getColumnIndex(columnName);
+ int actual = cursor.getInt(index);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Performs a query based on ID and returns a Cursor for the query.
+ *
+ * @param id The id of the download in DL Manager; pass -1 to query all downloads
+ * @return A cursor for the query results
+ */
+ protected Cursor getCursor(long id) throws Exception {
+ Query query = new Query();
+ if (id != -1) {
+ query.setFilterById(id);
+ }
+
+ Cursor cursor = mDownloadManager.query(query);
+ int currentWaitTime = 0;
+
+ try {
+ while (!cursor.moveToFirst()) {
+ Thread.sleep(DEFAULT_WAIT_POLL_TIME);
+ currentWaitTime += DEFAULT_WAIT_POLL_TIME;
+ if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
+ fail("timed out waiting for a non-null query result");
+ }
+ cursor.requery();
+ }
+ } catch (Exception e) {
+ cursor.close();
+ throw e;
+ }
+ return cursor;
+ }
+}
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
index e1d7b4c..ba5ee2c 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
@@ -18,7 +18,6 @@
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
-import android.app.DownloadManagerBaseTest;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
index 4c91ece..736b43c 100644
--- a/data/keyboards/AVRCP.kl
+++ b/data/keyboards/AVRCP.kl
@@ -14,8 +14,8 @@
# Key layout used for Bluetooth AVRCP support.
-key 200 MEDIA_PLAY_PAUSE WAKE
-key 201 MEDIA_PLAY_PAUSE WAKE
+key 200 MEDIA_PLAY WAKE
+key 201 MEDIA_PAUSE WAKE
key 166 MEDIA_STOP WAKE
key 163 MEDIA_NEXT WAKE
key 165 MEDIA_PREVIOUS WAKE
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index f0bf84c..8f40b48 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -42,8 +42,8 @@
<dd>Dumps a log of system
messages. The messages include a stack trace when the device throws an error,
as well as {@link android.util.Log} messages you've written from your application. To run
- logcat, execute <code>adb logcat</code> from your Android SDK {@code tools/} directory or,
-from DDMS, select <strong>Device > Run
+ logcat, execute <code>adb logcat</code> from your Android SDK {@code platform-tools/}
+directory or, from DDMS, select <strong>Device > Run
logcat</strong>. When using the <a href="{@docRoot}sdk/eclipse-adt.html">ADT plugin for
Eclipse</a>, you can also view logcat messages by opening the Logcat view, available from
<strong>Window > Show View > Other > Android > Logcat</strong>.
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 2e2d803..a4cec63 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -104,7 +104,8 @@
</ol>
<p>You can verify that your device is connected by executing <code>adb devices</code> from your
-SDK {@code tools/} directory. If connected, you'll see the device name listed as a "device."</p>
+SDK {@code platform-tools/} directory. If connected, you'll see the device name listed as a
+"device."</p>
<p>If using Eclipse, run or debug as usual. You will be presented
with a <b>Device Chooser</b> dialog that lists the available emulator(s) and connected device(s).
diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd
index 234b18f..d309f47 100644
--- a/docs/html/guide/developing/other-ide.jd
+++ b/docs/html/guide/developing/other-ide.jd
@@ -166,9 +166,10 @@
<p>Once you've created your project, you're ready to begin development.
You can move your project folder wherever you want for development, but keep in mind
that you must use the <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
-(adb) — located in the SDK <code>tools/</code> directory — to send your application
+(adb) — located in the SDK <code>platform-tools/</code> directory — to send your
+application
to the emulator (discussed later). So you need access between your project solution and
-the <code>tools/</code> folder.</p>
+the <code>platform-tools/</code> folder.</p>
<p class="caution"><strong>Caution:</strong> You should refrain from moving the
location of the SDK directory, because this will break the build scripts. (They
@@ -460,7 +461,7 @@
</li>
<li><strong>Install your application</strong>
- <p>From your SDK's <code>tools/</code> directory, install the {@code .apk} on the
+ <p>From your SDK's <code>platform-tools/</code> directory, install the {@code .apk} on the
emulator:
<pre>adb install <em><path_to_your_bin></em>.apk</pre>
<p>Your APK file (signed with either a release or debug key) is in your project {@code bin/}
@@ -507,7 +508,7 @@
Development</a> for more information.</p>
<p>Once your device is set up and connected via USB, navigate to your
-SDK's <code>tools/</code> directory and install the <code>.apk</code> on the device:
+SDK's <code>platform-tools/</code> directory and install the <code>.apk</code> on the device:
<pre>adb -d install <em>path/to/your/app</em>.apk</pre>
<p>The {@code -d} flag specifies that you want to use the attached device (in case you also
have an emulator running).</p>
diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd
index 04eed8e..3c6351e 100644
--- a/docs/html/guide/developing/tools/adb.jd
+++ b/docs/html/guide/developing/tools/adb.jd
@@ -79,6 +79,8 @@
<li>A daemon, which runs as a background process on each emulator or device instance. </li>
</ul>
+<p>You can find the {@code adb} tool in {@code <sdk>/platform-tools/}.</p>
+
<p>When you start an adb client, the client first checks whether there is an adb server process already running. If there isn't, it starts the server process. When the server starts, it binds to local TCP port 5037 and listens for commands sent from adb clients—all adb clients use port 5037 to communicate with the adb server. </p>
<p>The server then sets up connections to all running emulator/device instances. It locates emulator/device instances by scanning odd-numbered ports in the range 5555 to 5585, the range used by emulators/devices. Where the server finds an adb daemon, it sets up a connection to that port. Note that each emulator/device instance acquires a pair of sequential ports — an even-numbered port for console connections and an odd-numbered port for adb connections. For example: </p>
diff --git a/docs/html/guide/developing/tools/draw9patch.jd b/docs/html/guide/developing/tools/draw9patch.jd
index 61da1e0..4d8043b 100644
--- a/docs/html/guide/developing/tools/draw9patch.jd
+++ b/docs/html/guide/developing/tools/draw9patch.jd
@@ -4,8 +4,9 @@
<p>The Draw 9-patch tool allows you to easily create a
{@link android.graphics.NinePatch} graphic using a WYSIWYG editor.</p>
<p>For an introduction to Nine-patch graphics and how they work, please read
-the section on Nine-patch in the
-<a href="{@docRoot}guide/topics/resources/available-resources.html#ninepatch">Nine-patch Images</a> topic.</p>
+the section about Nine-patch in the
+<a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a>
+document.</p>
<img src="{@docRoot}images/draw9patch-norm.png" style="float:right" alt="" height="300" width="341"
/>
diff --git a/docs/html/guide/developing/tools/index.jd b/docs/html/guide/developing/tools/index.jd
index 899c0dc..b3e4625 100644
--- a/docs/html/guide/developing/tools/index.jd
+++ b/docs/html/guide/developing/tools/index.jd
@@ -55,7 +55,8 @@
<dd>The adb tool lets you install your application's .apk files on an
emulator or device and access the emulator or device from a command line.
You can also use it to link a standard debugger to application code running
- on an Android emulator or device.</dd>
+ on an Android emulator or device.
+ <p>This is located in {@code <sdk>/platform-tools/}.</p></dd>
<dt><a href="aapt.html">Android Asset
Packaging Tool</a> (aapt)</dt>
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index acb6f9f..5759be5 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -14,9 +14,9 @@
<li><a href="#drawables-from-xml">Creating from resource XML</a></li>
</ol>
</li>
- <li><a href="#shape-drawable">ShapeDrawable</a></li>
+ <li><a href="#shape-drawable">Shape Drawable</a></li>
<!-- <li><a href="#state-list">StateListDrawable</a></li> -->
- <li><a href="#nine-patch">NinePatchDrawable</a></li>
+ <li><a href="#nine-patch">Nine-patch</a></li>
<li><a href="#tween-animation">Tween Animation</a></li>
<li><a href="#frame-animation">Frame Animation</a></li>
</ol>
@@ -165,7 +165,7 @@
-<h2 id="shape-drawable">ShapeDrawable</h2>
+<h2 id="shape-drawable">Shape Drawable</h2>
<p>When you want to dynamically draw some two-dimensional graphics, a {@link android.graphics.drawable.ShapeDrawable}
object will probably suit your needs. With a ShapeDrawable, you can programmatically draw
@@ -234,6 +234,11 @@
Some properties you might want to adjust include
alpha transparency, color filter, dither, opacity and color.</p>
+<p>You can also define primitive drawable shapes using XML. For more information, see the
+section about Shape Drawables in the <a
+href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a>
+document.</p>
+
<!-- TODO
<h2 id="state-list">StateListDrawable</h2>
@@ -245,7 +250,7 @@
of the object it's attached to.
-->
-<h2 id="nine-patch">NinePatchDrawable</h2>
+<h2 id="nine-patch">Nine-patch</h2>
<p>A {@link android.graphics.drawable.NinePatchDrawable} graphic is a stretchable bitmap image, which Android
will automatically resize to accommodate the contents of the View in which you have placed it as the background.
@@ -424,8 +429,8 @@
<code>{@link android.view.View#setAnimation(android.view.animation.Animation) View.setAnimation()}</code>.
</p>
-<p>For more information on the XML syntax, available tags and attributes, see the discussion on animation
-in the <a href="{@docRoot}guide/topics/resources/available-resources.html#animation">Available Resources</a>.</p>
+<p>For more information on the XML syntax, available tags and attributes, see <a
+href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
<p class="note"><strong>Note:</strong> Regardless of how your animation may move or resize, the bounds of the
View that holds your animation will not automatically adjust to accommodate it. Even so, the animation will still
@@ -489,4 +494,6 @@
<code>{@link android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> method in
your Activity, which will get called when Android brings your window into focus.</p>
+<p>For more information on the XML syntax, available tags and attributes, see <a
+href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 488382e..9de247a 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -446,7 +446,7 @@
</tr>
<td colspan="3"><code>tools/</code></td>
<td>Contains the set of development and profiling tools that are platform-independent, such
-as the emulator, the AVD and SDK Manager, adb, ddms, hierarchyviewer and more. The tools in
+as the emulator, the AVD and SDK Manager, ddms, hierarchyviewer and more. The tools in
this directory may be updated at any time (from the <em>Android SDK Tools</em> component),
independent of platform releases, whereas the tools in {@code platform-tools/} may be updated based
on the latest platform release.</td>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e3bb6eb..b739d83 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -559,7 +559,7 @@
}
/**
- * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
+ * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
*
* @param fakeBoldText true to set the fakeBoldText bit in the paint's
* flags, false to clear it.
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 1bddbae..3ff483d 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -102,16 +102,6 @@
mRS.nAllocationSyncAll(getID(), srcLocation);
}
- public void uploadToTexture(int baseMipLevel) {
- mRS.validate();
- mRS.nAllocationUploadToTexture(getID(), false, baseMipLevel);
- }
-
- public void uploadToTexture(boolean genMips, int baseMipLevel) {
- mRS.validate();
- mRS.nAllocationUploadToTexture(getID(), genMips, baseMipLevel);
- }
-
public void uploadToBufferObject() {
mRS.validate();
mRS.nAllocationUploadToBufferObject(getID());
@@ -390,6 +380,10 @@
return new Allocation(id, rs, type, usage);
}
+ static public Allocation createTyped(RenderScript rs, Type type, int usage) {
+ return createTyped(rs, type, MipmapControl.MIPMAP_NONE, usage);
+ }
+
static public Allocation createTyped(RenderScript rs, Type type) {
return createTyped(rs, type, MipmapControl.MIPMAP_NONE, USAGE_SCRIPT);
}
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index ff3e22b..ed16451 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -248,24 +248,45 @@
addU32(v.w);
}
+ // to be removed on cleanup
public void addObj(Matrix4f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ // to be removed on cleanup
public void addObj(Matrix3f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ // to be removed on cleanup
public void addObj(Matrix2f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ public void addMatrix(Matrix4f v) {
+ for (int i=0; i < v.mMat.length; i++) {
+ addF32(v.mMat[i]);
+ }
+ }
+
+ public void addMatrix(Matrix3f v) {
+ for (int i=0; i < v.mMat.length; i++) {
+ addF32(v.mMat[i]);
+ }
+ }
+
+ public void addMatrix(Matrix2f v) {
+ for (int i=0; i < v.mMat.length; i++) {
+ addF32(v.mMat[i]);
+ }
+ }
+
public void addBoolean(boolean v) {
addI8((byte)(v ? 1 : 0));
}
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index af85d8e..c3e5faf 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -16,11 +16,12 @@
package android.renderscript;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import android.content.res.Resources;
import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
@@ -32,28 +33,33 @@
**/
public class FileA3D extends BaseObj {
+ // This will go away in the clean up pass,
+ // trying to avoid multiproject submits
public enum ClassID {
UNKNOWN,
- MESH,
- TYPE,
- ELEMENT,
- ALLOCATION,
- PROGRAM_VERTEX,
- PROGRAM_RASTER,
- PROGRAM_FRAGMENT,
- PROGRAM_STORE,
- SAMPLER,
- ANIMATION,
- ADAPTER_1D,
- ADAPTER_2D,
- SCRIPT_C;
+ MESH;
public static ClassID toClassID(int intID) {
return ClassID.values()[intID];
}
}
+ public enum EntryType {
+
+ UNKNOWN (0),
+ MESH (1);
+
+ int mID;
+ EntryType(int id) {
+ mID = id;
+ }
+
+ static EntryType toEntryType(int intID) {
+ return EntryType.values()[intID];
+ }
+ }
+
// Read only class with index entries
public static class IndexEntry {
RenderScript mRS;
@@ -61,6 +67,7 @@
int mID;
String mName;
ClassID mClassID;
+ EntryType mEntryType;
BaseObj mLoadedObj;
public String getName() {
@@ -71,18 +78,27 @@
return mClassID;
}
+ public EntryType getEntryType() {
+ return mEntryType;
+ }
+
public BaseObj getObject() {
mRS.validate();
BaseObj obj = internalCreate(mRS, this);
return obj;
}
+ public Mesh getMesh() {
+ return (Mesh)getObject();
+ }
+
static synchronized BaseObj internalCreate(RenderScript rs, IndexEntry entry) {
if(entry.mLoadedObj != null) {
return entry.mLoadedObj;
}
- if(entry.mClassID == ClassID.UNKNOWN) {
+ // to be purged on cleanup
+ if(entry.mEntryType == EntryType.UNKNOWN) {
return null;
}
@@ -91,51 +107,23 @@
return null;
}
- switch (entry.mClassID) {
+ switch (entry.mEntryType) {
case MESH:
entry.mLoadedObj = new Mesh(objectID, rs);
break;
- case TYPE:
- entry.mLoadedObj = new Type(objectID, rs);
- break;
- case ELEMENT:
- entry.mLoadedObj = null;
- break;
- case ALLOCATION:
- entry.mLoadedObj = null;
- break;
- case PROGRAM_VERTEX:
- entry.mLoadedObj = new ProgramVertex(objectID, rs);
- break;
- case PROGRAM_RASTER:
- break;
- case PROGRAM_FRAGMENT:
- break;
- case PROGRAM_STORE:
- break;
- case SAMPLER:
- break;
- case ANIMATION:
- break;
- case ADAPTER_1D:
- break;
- case ADAPTER_2D:
- break;
- case SCRIPT_C:
- break;
}
entry.mLoadedObj.updateFromNative();
-
return entry.mLoadedObj;
}
- IndexEntry(RenderScript rs, int index, int id, String name, ClassID classID) {
+ IndexEntry(RenderScript rs, int index, int id, String name, EntryType type) {
mRS = rs;
mIndex = index;
mID = id;
mName = name;
- mClassID = classID;
+ mEntryType = type;
+ mClassID = mEntryType == EntryType.MESH ? ClassID.MESH : ClassID.UNKNOWN;
mLoadedObj = null;
}
}
@@ -161,11 +149,11 @@
mRS.nFileA3DGetIndexEntries(getID(), numFileEntries, ids, names);
for(int i = 0; i < numFileEntries; i ++) {
- mFileEntries[i] = new IndexEntry(mRS, i, getID(), names[i], ClassID.toClassID(ids[i]));
+ mFileEntries[i] = new IndexEntry(mRS, i, getID(), names[i], EntryType.toEntryType(ids[i]));
}
}
- public int getNumIndexEntries() {
+ public int getIndexEntryCount() {
if(mFileEntries == null) {
return 0;
}
@@ -173,12 +161,29 @@
}
public IndexEntry getIndexEntry(int index) {
- if(getNumIndexEntries() == 0 || index < 0 || index >= mFileEntries.length) {
+ if(getIndexEntryCount() == 0 || index < 0 || index >= mFileEntries.length) {
return null;
}
return mFileEntries[index];
}
+ // API cleanup stand-ins
+ // TODO: implement ermaining loading mechanisms
+ static public FileA3D createFromAsset(RenderScript rs, AssetManager mgr, String path)
+ throws IllegalArgumentException {
+ return null;
+ }
+
+ static public FileA3D createFromFile(RenderScript rs, String path)
+ throws IllegalArgumentException {
+ return null;
+ }
+
+ static public FileA3D createFromFile(RenderScript rs, File path)
+ throws IllegalArgumentException {
+ return createFromFile(rs, path.getAbsolutePath());
+ }
+
static public FileA3D createFromResource(RenderScript rs, Resources res, int id)
throws IllegalArgumentException {
diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java
index de25014..0f7c24d 100644
--- a/graphics/java/android/renderscript/Font.java
+++ b/graphics/java/android/renderscript/Font.java
@@ -16,13 +16,16 @@
package android.renderscript;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Map;
import java.util.HashMap;
+import java.util.Map;
-import android.content.res.Resources;
+import android.os.Environment;
+
import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.util.Log;
import android.util.TypedValue;
@@ -126,13 +129,13 @@
/**
* Takes a specific file name as an argument
*/
- static public Font create(RenderScript rs, Resources res, String fileName, int size)
+ static public Font createFromFile(RenderScript rs, Resources res, String path, float pointSize)
throws IllegalArgumentException {
rs.validate();
try {
int dpi = res.getDisplayMetrics().densityDpi;
- int fontId = rs.nFontCreateFromFile(fileName, size, dpi);
+ int fontId = rs.nFontCreateFromFile(path, pointSize, dpi);
if(fontId == 0) {
throw new IllegalStateException("Failed loading a font");
@@ -148,6 +151,21 @@
return null;
}
+ static public Font createFromFile(RenderScript rs, Resources res, File path, float pointSize)
+ throws IllegalArgumentException {
+ return createFromFile(rs, res, path.getAbsolutePath(), pointSize);
+ }
+
+ static public Font createFromAsset(RenderScript rs, Resources res, AssetManager mgr, String path, float pointSize)
+ throws IllegalArgumentException {
+ return null;
+ }
+
+ static public Font createFromResource(RenderScript rs, Resources res, int id, float pointSize)
+ throws IllegalArgumentException {
+ return null;
+ }
+
/**
* Accepts one of the following family names as an argument
* and will attemp to produce the best match with a system font
@@ -157,9 +175,12 @@
* "monospace" "courier" "courier new" "monaco"
* Returns default font if no match could be found
*/
- static public Font createFromFamily(RenderScript rs, Resources res, String familyName, Style fontStyle, int size)
+ static public Font create(RenderScript rs, Resources res, String familyName, Style fontStyle, float pointSize)
throws IllegalArgumentException {
String fileName = getFontFileName(familyName, fontStyle);
- return create(rs, res, fileName, size);
+ String fontPath = Environment.getRootDirectory().getAbsolutePath();
+ fontPath += "/fonts/" + fileName;
+ return createFromFile(rs, res, fontPath, pointSize);
}
+
}
diff --git a/graphics/java/android/renderscript/Matrix2f.java b/graphics/java/android/renderscript/Matrix2f.java
index 99d23db..4654c48 100644
--- a/graphics/java/android/renderscript/Matrix2f.java
+++ b/graphics/java/android/renderscript/Matrix2f.java
@@ -31,6 +31,15 @@
loadIdentity();
}
+ public Matrix2f(float[] dataArray) {
+ mMat = new float[2];
+ System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
+ }
+
+ public float[] getArray() {
+ return mMat;
+ }
+
public float get(int i, int j) {
return mMat[i*2 + j];
}
diff --git a/graphics/java/android/renderscript/Matrix3f.java b/graphics/java/android/renderscript/Matrix3f.java
index 961bc5d..15e5ce6 100644
--- a/graphics/java/android/renderscript/Matrix3f.java
+++ b/graphics/java/android/renderscript/Matrix3f.java
@@ -31,6 +31,15 @@
loadIdentity();
}
+ public Matrix3f(float[] dataArray) {
+ mMat = new float[9];
+ System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
+ }
+
+ public float[] getArray() {
+ return mMat;
+ }
+
public float get(int i, int j) {
return mMat[i*3 + j];
}
diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java
index 5ffc21a..ea97509 100644
--- a/graphics/java/android/renderscript/Matrix4f.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -31,6 +31,15 @@
loadIdentity();
}
+ public Matrix4f(float[] dataArray) {
+ mMat = new float[16];
+ System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
+ }
+
+ public float[] getArray() {
+ return mMat;
+ }
+
public float get(int i, int j) {
return mMat[i*4 + j];
}
@@ -147,6 +156,10 @@
mMat[14]= -(f + n) / (f - n);
}
+ public void loadOrthoWindow(int w, int h) {
+ loadOrtho(0,w, h,0, -1,1);
+ }
+
public void loadFrustum(float l, float r, float b, float t, float n, float f) {
loadIdentity();
mMat[0] = 2 * n / (r - l);
@@ -159,6 +172,14 @@
mMat[15]= 0;
}
+ public void loadPerspective(float fovy, float aspect, float near, float far) {
+ float top = near * (float)Math.tan((float) (fovy * Math.PI / 360.0f));
+ float bottom = -top;
+ float left = bottom * aspect;
+ float right = top * aspect;
+ loadFrustum(left, right, bottom, top, near, far);
+ }
+
public void multiply(Matrix4f rhs) {
Matrix4f tmp = new Matrix4f();
tmp.loadMultiply(this, rhs);
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 5f93f5b..0b7262b 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -305,8 +305,8 @@
return rsnFileA3DGetEntryByIndex(mContext, fileA3D, index);
}
- native int rsnFontCreateFromFile(int con, String fileName, int size, int dpi);
- synchronized int nFontCreateFromFile(String fileName, int size, int dpi) {
+ native int rsnFontCreateFromFile(int con, String fileName, float size, int dpi);
+ synchronized int nFontCreateFromFile(String fileName, float size, int dpi) {
return rsnFontCreateFromFile(mContext, fileName, size, dpi);
}
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index a8343b3..493653a 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -623,7 +623,7 @@
// -----------------------------------
static int
-nFontCreateFromFile(JNIEnv *_env, jobject _this, RsContext con, jstring fileName, jint fontSize, jint dpi)
+nFontCreateFromFile(JNIEnv *_env, jobject _this, RsContext con, jstring fileName, jfloat fontSize, jint dpi)
{
const char* fileNameUTF = _env->GetStringUTFChars(fileName, NULL);
@@ -1239,7 +1239,7 @@
{"rsnFileA3DGetIndexEntries", "(III[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries },
{"rsnFileA3DGetEntryByIndex", "(III)I", (void*)nFileA3DGetEntryByIndex },
-{"rsnFontCreateFromFile", "(ILjava/lang/String;II)I", (void*)nFontCreateFromFile },
+{"rsnFontCreateFromFile", "(ILjava/lang/String;FI)I", (void*)nFontCreateFromFile },
{"rsnElementCreate", "(IIIZI)I", (void*)nElementCreate },
{"rsnElementCreate2", "(I[I[Ljava/lang/String;[I)I", (void*)nElementCreate2 },
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index 74c9d5d..df5be32 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -71,7 +71,8 @@
bool addStringTag(const char* name, const char* value);
void endFile();
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
+ virtual bool scanFile(const char* path, long long lastModified,
+ long long fileSize, bool isDirectory) = 0;
virtual bool handleStringTag(const char* name, const char* value) = 0;
virtual bool setMimeType(const char* mimeType) = 0;
virtual bool addNoMediaFolder(const char* path) = 0;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 940470d..b3815c4 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -14,11 +14,12 @@
struct ACodec : public AHierarchicalStateMachine {
enum {
- kWhatFillThisBuffer = 'fill',
- kWhatDrainThisBuffer = 'drai',
- kWhatEOS = 'eos ',
- kWhatShutdownCompleted = 'scom',
- kWhatFlushCompleted = 'fcom',
+ kWhatFillThisBuffer = 'fill',
+ kWhatDrainThisBuffer = 'drai',
+ kWhatEOS = 'eos ',
+ kWhatShutdownCompleted = 'scom',
+ kWhatFlushCompleted = 'fcom',
+ kWhatOutputFormatChanged = 'outC',
};
ACodec();
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 941f6b9..91ec60c 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -64,6 +64,9 @@
void post(int64_t delayUs = 0);
+ // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
+ // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
+ // their refcount incremented.
sp<AMessage> dup() const;
AString debugString(int32_t indent = 0) const;
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
index 6cb50b8..7d99686 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.renderscript.*;
import android.renderscript.Element.Builder;
+import android.renderscript.Font.Style;
import android.renderscript.ProgramStore.DepthFunc;
import android.util.Log;
@@ -186,22 +187,20 @@
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
- if (entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+ if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
Log.e("rs", "could not load model");
} else {
mMesh = (Mesh)entry.getObject();
mScript.set_gTestMesh(mMesh);
}
- mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8);
+ mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
mScript.set_gItalic(mItalic);
initTextAllocation();
initTransformHierarchy();
- Log.v("========SceneGraph========", "transform hierarchy initialized");
-
mScript.bind_gRootNode(mRootTransform.getField());
mScript.bind_gGroup(mGroup1.mParent.mChildField);
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
index 747463a..5451ca1 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -148,14 +148,14 @@
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
- if (entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+ if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
Log.e("rs", "could not load model");
} else {
mMesh = (Mesh)entry.getObject();
mScript.set_gTestMesh(mMesh);
}
- mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8);
+ mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
mScript.set_gItalic(mItalic);
initTextAllocation();
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
index ddb05b3..b3e8026 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
@@ -315,22 +315,15 @@
private void initFonts() {
// Sans font by family name
- mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif",
- Font.Style.NORMAL, 8);
- // Create font by file name
- mFontSerif = Font.create(mRS, mRes, "DroidSerif-Regular.ttf", 8);
+ mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+ mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8);
// Create fonts by family and style
- mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif",
- Font.Style.BOLD, 8);
- mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif",
- Font.Style.ITALIC, 8);
- mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif",
- Font.Style.BOLD_ITALIC, 8);
- mFontMono = Font.createFromFamily(mRS, mRes, "mono",
- Font.Style.NORMAL, 8);
+ mFontSerifBold = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8);
+ mFontSerifItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
+ mFontSerifBoldItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+ mFontMono = Font.create(mRS, mRes, "mono", Font.Style.NORMAL, 8);
- mTextAlloc = Allocation.createFromString(mRS, "String from allocation",
- Allocation.USAGE_SCRIPT);
+ mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT);
mScript.set_gFontSans(mFontSans);
mScript.set_gFontSerif(mFontSerif);
@@ -351,7 +344,7 @@
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
- if (entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+ if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
Log.e("rs", "could not load model");
} else {
mTorus = (Mesh)entry.getObject();
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsListRS.java b/libs/rs/java/Samples/src/com/android/samples/RsListRS.java
index 223f552..8e2d51f 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsListRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsListRS.java
@@ -134,7 +134,7 @@
mScript.bind_gList(mListAllocs);
- mItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+ mItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
mScript.set_gItalic(mItalic);
mRS.bindRootScript(mScript);
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index 75e8d99..636a486 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -23,6 +23,7 @@
import android.graphics.BitmapFactory;
import android.renderscript.*;
import android.renderscript.Allocation.CubemapLayout;
+import android.renderscript.Font.Style;
import android.renderscript.Program.TextureType;
import android.renderscript.ProgramStore.DepthFunc;
import android.renderscript.Sampler.Value;
@@ -304,14 +305,13 @@
private void initFonts() {
// Sans font by family name
- mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
- // Create font by file name
- mFontSerif = Font.create(mRS, mRes, "DroidSerif-Regular.ttf", 8);
+ mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+ mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8);
// Create fonts by family and style
- mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
- mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.ITALIC, 8);
- mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
- mFontMono = Font.createFromFamily(mRS, mRes, "mono", Font.Style.NORMAL, 8);
+ mFontSerifBold = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8);
+ mFontSerifItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
+ mFontSerifBoldItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+ mFontMono = Font.create(mRS, mRes, "mono", Font.Style.NORMAL, 8);
mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT);
@@ -330,7 +330,7 @@
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
- if (entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+ if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
Log.e("rs", "could not load model");
} else {
mTorus = (Mesh)entry.getObject();
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index 265e1d6..a50321e 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -94,7 +94,7 @@
mScript.bind_gList(mListAllocs);
- mFont = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
+ mFont = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8);
mScript.set_gFont(mFont);
mRS.bindRootScript(mScript);
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 5daba08..cf94060 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -420,7 +420,7 @@
FontCreateFromFile {
param const char *name
- param uint32_t fontSize
+ param float fontSize
param uint32_t dpi
ret RsFont
}
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 2fa1f0a..80bca43 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -40,20 +40,15 @@
mFace = NULL;
}
-bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi) {
+bool Font::init(const char *name, float fontSize, uint32_t dpi) {
if (mInitialized) {
LOGE("Reinitialization of fonts not supported");
return false;
}
- String8 fontsDir("/fonts/");
- String8 fullPath(getenv("ANDROID_ROOT"));
- fullPath += fontsDir;
- fullPath += name;
-
- FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), fullPath.string(), 0, &mFace);
+ FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace);
if (error) {
- LOGE("Unable to initialize font %s", fullPath.string());
+ LOGE("Unable to initialize font %s", name);
return false;
}
@@ -61,9 +56,9 @@
mFontSize = fontSize;
mDpi = dpi;
- error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
+ error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0);
if (error) {
- LOGE("Unable to set font size on %s", fullPath.string());
+ LOGE("Unable to set font size on %s", name);
return false;
}
@@ -278,7 +273,7 @@
return newGlyph;
}
-Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi) {
+Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi) {
rsc->mStateFont.checkInit();
Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
@@ -332,31 +327,20 @@
// Get the gamma
float gamma = DEFAULT_TEXT_GAMMA;
if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
- LOGD(" Setting text gamma to %s", property);
gamma = atof(property);
- } else {
- LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
}
// Get the black gamma threshold
int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
- LOGD(" Setting text black gamma threshold to %s", property);
blackThreshold = atoi(property);
- } else {
- LOGD(" Using default text black gamma threshold of %d",
- DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
}
mBlackThreshold = (float)(blackThreshold) / 255.0f;
// Get the white gamma threshold
int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
- LOGD(" Setting text white gamma threshold to %s", property);
whiteThreshold = atoi(property);
- } else {
- LOGD(" Using default white black gamma threshold of %d",
- DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
}
mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
@@ -735,7 +719,11 @@
Font *currentFont = mRSC->getFont();
if (!currentFont) {
if (!mDefault.get()) {
- mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
+ String8 fontsDir("/fonts/DroidSans.ttf");
+ String8 fullPath(getenv("ANDROID_ROOT"));
+ fullPath += fontsDir;
+
+ mDefault.set(Font::create(mRSC, fullPath.string(), 16, 96));
}
currentFont = mDefault.get();
}
@@ -815,7 +803,7 @@
namespace android {
namespace renderscript {
-RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi) {
+RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, float fontSize, uint32_t dpi) {
Font *newFont = Font::create(rsc, name, fontSize, dpi);
if (newFont) {
newFont->incUserRef();
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index 0f6815d..c24c9f1 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -73,7 +73,7 @@
return RS_A3D_CLASS_ID_UNKNOWN;
}
- static Font * create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi);
+ static Font * create(Context *rsc, const char *name, float fontSize, uint32_t dpi);
protected:
@@ -112,11 +112,11 @@
};
String8 mFontName;
- uint32_t mFontSize;
+ float mFontSize;
uint32_t mDpi;
Font(Context *rsc);
- bool init(const char *name, uint32_t fontSize, uint32_t dpi);
+ bool init(const char *name, float fontSize, uint32_t dpi);
FT_Face mFace;
bool mInitialized;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 507430d..0ae85cb 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -420,7 +420,14 @@
} else {
// bccReadBC returns a neagative value: Didn't read any script,
// So, use cached binary instead
- bccLoadBinary(s->mBccScript);
+ if (bccLoadBinary(s->mBccScript)) { // LoadBinary fails ==> Recompile
+ bccReadBC(s->mBccScript,
+ s->mEnviroment.mScriptText,
+ s->mEnviroment.mScriptTextLength,
+ NULL,
+ cacheDir);
+ bccCompileBC(s->mBccScript);
+ }
}
bccGetScriptLabel(s->mBccScript, "root", (BCCvoid**) &s->mProgram.mRoot);
bccGetScriptLabel(s->mBccScript, "init", (BCCvoid**) &s->mProgram.mInit);
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 63ec6b2..ae6dd61 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -388,7 +388,14 @@
String prop = SystemProperties.get("drm.service.enabled");
return prop != null && prop.equals("true");
}
-
+
+ private final String mediaToExternalPath(String path) {
+ if (mMediaStoragePath != null && path.startsWith(mMediaStoragePath)) {
+ path = mExternalStoragePath + path.substring(mMediaStoragePath.length());
+ }
+ return path;
+ }
+
private class MyMediaScannerClient implements MediaScannerClient {
private String mArtist;
@@ -407,70 +414,74 @@
private long mFileSize;
private String mWriter;
- public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
-
- // special case certain file names
- // I use regionMatches() instead of substring() below
- // to avoid memory allocation
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
- // ignore those ._* files created by MacOS
- if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
- return null;
- }
-
- // ignore album art files created by Windows Media Player:
- // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg and AlbumArt_{...}_Small.jpg
- if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
- if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
- path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
- return null;
- }
- int length = path.length() - lastSlash - 1;
- if ((length == 17 && path.regionMatches(true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
- (length == 10 && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
- return null;
- }
- }
- }
-
+ public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
+ long fileSize, boolean isDirectory) {
mMimeType = mimeType;
mFileType = 0;
mFileSize = fileSize;
- // try mimeType first, if it is specified
- if (mimeType != null) {
- mFileType = MediaFile.getFileTypeForMimeType(mimeType);
- }
-
- // if mimeType was not specified, compute file type based on file extension.
- if (mFileType == 0) {
- MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
- if (mediaFileType != null) {
- mFileType = mediaFileType.fileType;
- if (mMimeType == null) {
- mMimeType = mediaFileType.mimeType;
+ if (!isDirectory) {
+ // special case certain file names
+ // I use regionMatches() instead of substring() below
+ // to avoid memory allocation
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
+ // ignore those ._* files created by MacOS
+ if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
+ return null;
}
+
+ // ignore album art files created by Windows Media Player:
+ // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
+ // and AlbumArt_{...}_Small.jpg
+ if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
+ if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
+ path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
+ return null;
+ }
+ int length = path.length() - lastSlash - 1;
+ if ((length == 17 && path.regionMatches(
+ true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
+ (length == 10
+ && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
+ return null;
+ }
+ }
+ }
+
+ // try mimeType first, if it is specified
+ if (mimeType != null) {
+ mFileType = MediaFile.getFileTypeForMimeType(mimeType);
+ }
+
+ // if mimeType was not specified, compute file type based on file extension.
+ if (mFileType == 0) {
+ MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+ if (mediaFileType != null) {
+ mFileType = mediaFileType.fileType;
+ if (mMimeType == null) {
+ mMimeType = mediaFileType.mimeType;
+ }
+ }
+ }
+
+ if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
+ mFileType = getFileTypeFromDrm(path);
}
}
- if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
- mFileType = getFileTypeFromDrm(path);
- }
-
- String key = path;
- if (mMediaStoragePath != null && key.startsWith(mMediaStoragePath)) {
- // MediaProvider uses external variant of path for _data, so we need to match
- // against that path instead.
- key = mExternalStoragePath + key.substring(mMediaStoragePath.length());
- }
+ // MediaProvider uses external variant of path for _data, so we need to match
+ // against that path instead.
+ String key = mediaToExternalPath(path);
if (mCaseInsensitivePaths) {
key = path.toLowerCase();
}
FileCacheEntry entry = mFileCache.get(key);
if (entry == null) {
Uri tableUri;
- if (MediaFile.isVideoFileType(mFileType)) {
+ if (isDirectory) {
+ tableUri = mFilesUri;
+ } else if (MediaFile.isVideoFileType(mFileType)) {
tableUri = mVideoUri;
} else if (MediaFile.isImageFileType(mFileType)) {
tableUri = mImagesUri;
@@ -479,7 +490,8 @@
} else {
tableUri = mFilesUri;
}
- entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
+ entry = new FileCacheEntry(tableUri, 0, path, 0,
+ (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
mFileCache.put(key, entry);
}
entry.mSeenInFileSystem = true;
@@ -514,22 +526,19 @@
return entry;
}
- public void scanFile(String path, long lastModified, long fileSize) {
+ public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory) {
// This is the callback funtion from native codes.
// Log.v(TAG, "scanFile: "+path);
- doScanFile(path, null, lastModified, fileSize, false);
- }
-
- public void scanFile(String path, String mimeType, long lastModified, long fileSize) {
- doScanFile(path, mimeType, lastModified, fileSize, false);
+ doScanFile(path, null, lastModified, fileSize, isDirectory, false);
}
public Uri doScanFile(String path, String mimeType, long lastModified,
- long fileSize, boolean scanAlways) {
+ long fileSize, boolean isDirectory, boolean scanAlways) {
Uri result = null;
// long t1 = System.currentTimeMillis();
try {
- FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);
+ FileCacheEntry entry = beginFile(path, mimeType, lastModified,
+ fileSize, isDirectory);
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
String lowpath = path.toLowerCase();
@@ -775,7 +784,11 @@
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
}
if (tableUri == mFilesUri) {
- values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
+ int format = entry.mFormat;
+ if (format == 0) {
+ format = MediaFile.getFormatCode(entry.mPath, mMimeType);
+ }
+ values.put(Files.FileColumns.FORMAT, format);
}
// new file, insert it
result = mMediaProvider.insert(tableUri, values);
@@ -872,6 +885,8 @@
}
public void addNoMediaFolder(String path) {
+ path = mediaToExternalPath(path);
+
ContentValues values = new ContentValues();
values.put(MediaStore.Images.ImageColumns.DATA, "");
String [] pathSpec = new String[] {path + '%'};
@@ -930,11 +945,9 @@
}
if (filePath != null) {
- if (mMediaStoragePath != null && filePath.startsWith(mMediaStoragePath)) {
- // MediaProvider uses external variant of path for _data, so we need to query
- // using that path instead.
- filePath = mExternalStoragePath + filePath.substring(mMediaStoragePath.length());
- }
+ // MediaProvider uses external variant of path for _data, so we need to query
+ // using that path instead.
+ filePath = mediaToExternalPath(filePath);
// query for only one file
where = Files.FileColumns.DATA + "=?";
@@ -1060,8 +1073,7 @@
boolean fileMissing = false;
if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
- if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
- inScanDirectory(path, directories)) {
+ if (inScanDirectory(path, directories)) {
// we didn't see this file in the scan directory.
fileMissing = true;
} else {
@@ -1180,7 +1192,7 @@
long lastModifiedSeconds = file.lastModified() / 1000;
// always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
+ return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),false, true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
@@ -1204,12 +1216,9 @@
// build file cache so we can look up tracks in the playlist
prescan(null, true);
- String key = path;
- if (mMediaStoragePath != null && key.startsWith(mMediaStoragePath)) {
- // MediaProvider uses external variant of path for _data, so we need to match
- // against that path instead.
- key = mExternalStoragePath + key.substring(mMediaStoragePath.length());
- }
+ // MediaProvider uses external variant of path for _data, so we need to match
+ // against that path instead.
+ String key = mediaToExternalPath(path);
if (mCaseInsensitivePaths) {
key = path.toLowerCase();
}
@@ -1227,7 +1236,8 @@
long lastModifiedSeconds = file.lastModified() / 1000;
// always scan the file, so we can return the content://media Uri for existing files
- mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
+ mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
+ (format == MtpConstants.FORMAT_ASSOCIATION), true);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java
index 258c3b4..ac326ef 100644
--- a/media/java/android/media/MediaScannerClient.java
+++ b/media/java/android/media/MediaScannerClient.java
@@ -21,9 +21,7 @@
*/
public interface MediaScannerClient
{
- public void scanFile(String path, long lastModified, long fileSize);
-
- public void scanFile(String path, String mimeType, long lastModified, long fileSize);
+ public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory);
public void addNoMediaFolder(String path);
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 0edd449..b5a82e3 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -160,7 +160,21 @@
// Ignore failures while cleaning up.
}
}
- if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {
+
+ if (bitmap == null) return null;
+
+ if (kind == Images.Thumbnails.MINI_KIND) {
+ // Scale down the bitmap if it's too large.
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int max = Math.max(width, height);
+ if (max > 512) {
+ float scale = 512f / max;
+ int w = Math.round(scale * width);
+ int h = Math.round(scale * height);
+ bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+ }
+ } else if (kind == Images.Thumbnails.MICRO_KIND) {
bitmap = extractThumbnail(bitmap,
TARGET_SIZE_MICRO_THUMBNAIL,
TARGET_SIZE_MICRO_THUMBNAIL,
@@ -282,7 +296,7 @@
private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
BitmapFactory.Options options) {
- Bitmap b = null;
+ Bitmap b = null;
try {
if (pfd == null) pfd = makeInputStream(uri, cr);
if (pfd == null) return null;
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index fd0b233..a5176fa 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -62,7 +62,7 @@
}
else {
mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile",
- "(Ljava/lang/String;JJ)V");
+ "(Ljava/lang/String;JJZ)V");
mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag",
"(Ljava/lang/String;Ljava/lang/String;)V");
mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
@@ -78,12 +78,14 @@
}
// returns true if it succeeded, false if an exception occured in the Java code
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
+ virtual bool scanFile(const char* path, long long lastModified,
+ long long fileSize, bool isDirectory)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
- mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
+ mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
+ fileSize, isDirectory);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index c31b622..5ec573e 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -84,6 +84,7 @@
// place to copy file or directory name
char* fileSpot = path + strlen(path);
struct dirent* entry;
+ struct stat statbuf;
// ignore directories that contain a ".nomedia" file
if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
@@ -125,7 +126,6 @@
// If the type is unknown, stat() the file instead.
// This is sometimes necessary when accessing NFS mounted filesystems, but
// could be needed in other cases well.
- struct stat statbuf;
if (stat(path, &statbuf) == 0) {
if (S_ISREG(statbuf.st_mode)) {
type = DT_REG;
@@ -142,8 +142,15 @@
// for example, the Mac ".Trashes" directory
if (name[0] == '.') continue;
+ // report the directory to the client
+ if (stat(path, &statbuf) == 0) {
+ client.scanFile(path, statbuf.st_mtime, 0, true);
+ }
+
+ // and now process its contents
strcat(fileSpot, "/");
- int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
+ int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
+ exceptionCheck, exceptionEnv);
if (err) {
// pass exceptions up - ignore other errors
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
@@ -151,11 +158,8 @@
continue;
}
} else {
- struct stat statbuf;
stat(path, &statbuf);
- if (statbuf.st_size > 0) {
- client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
- }
+ client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false);
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
}
}
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
index 89a5e69..9738e33 100644
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -166,6 +166,9 @@
sp<MediaSource> mDecoder;
sp<AMessage> mNotify;
bool mEOS;
+ bool mSentFormat;
+
+ void sendFormatChange();
DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
};
@@ -174,7 +177,8 @@
const sp<MediaSource> &decoder, const sp<AMessage> ¬ify)
: mDecoder(decoder),
mNotify(notify),
- mEOS(false) {
+ mEOS(false),
+ mSentFormat(false) {
}
DecoderWrapper::WrapperReader::~WrapperReader() {
@@ -215,12 +219,17 @@
MediaBuffer *src;
status_t err = mDecoder->read(&src, &options);
- sp<AMessage> notify = mNotify->dup();
-
- sp<AMessage> realNotify;
- CHECK(notify->findMessage("real-notify", &realNotify));
-
if (err == OK) {
+ if (!mSentFormat) {
+ sendFormatChange();
+ mSentFormat = true;
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+
+ sp<AMessage> realNotify;
+ CHECK(notify->findMessage("real-notify", &realNotify));
+
realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
sp<ABuffer> dst = new ABuffer(src->range_length());
@@ -236,12 +245,23 @@
dst->meta()->setInt64("timeUs", timeUs);
realNotify->setObject("buffer", dst);
+
+ notify->post();
+ } else if (err == INFO_FORMAT_CHANGED) {
+ sendFormatChange();
+
+ readMore(false /* flush */);
} else {
+ sp<AMessage> notify = mNotify->dup();
+
+ sp<AMessage> realNotify;
+ CHECK(notify->findMessage("real-notify", &realNotify));
+
realNotify->setInt32("what", ACodec::kWhatEOS);
mEOS = true;
- }
- notify->post();
+ notify->post();
+ }
break;
}
@@ -251,6 +271,46 @@
}
}
+void DecoderWrapper::WrapperReader::sendFormatChange() {
+ sp<AMessage> notify = mNotify->dup();
+
+ sp<AMessage> realNotify;
+ CHECK(notify->findMessage("real-notify", &realNotify));
+
+ realNotify->setInt32("what", ACodec::kWhatOutputFormatChanged);
+
+ sp<MetaData> meta = mDecoder->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ realNotify->setString("mime", mime);
+
+ if (!strncasecmp("audio/", mime, 6)) {
+ int32_t numChannels;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+
+ int32_t sampleRate;
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ realNotify->setInt32("channel-count", numChannels);
+ realNotify->setInt32("sample-rate", sampleRate);
+ } else {
+ CHECK(!strncasecmp("video/", mime, 6));
+
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ realNotify->setInt32("width", width);
+ realNotify->setInt32("height", height);
+ }
+
+ notify->post();
+
+ mSentFormat = true;
+}
+
////////////////////////////////////////////////////////////////////////////////
DecoderWrapper::DecoderWrapper()
@@ -327,24 +387,33 @@
case kWhatFillBufferDone:
{
- CHECK_GT(mNumPendingDecodes, 0);
- --mNumPendingDecodes;
-
- if (mFlushing) {
- completeFlushIfPossible();
- break;
- }
-
sp<AMessage> notify;
CHECK(msg->findMessage("real-notify", ¬ify));
- sp<AMessage> reply =
- new AMessage(kWhatOutputBufferDrained, id());
+ int32_t what;
+ CHECK(notify->findInt32("what", &what));
- notify->setMessage("reply", reply);
+ if (what == ACodec::kWhatDrainThisBuffer) {
+ CHECK_GT(mNumPendingDecodes, 0);
+ --mNumPendingDecodes;
+
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, id());
+
+ notify->setMessage("reply", reply);
+
+ ++mNumOutstandingOutputBuffers;
+ } else if (what == ACodec::kWhatEOS) {
+ CHECK_GT(mNumPendingDecodes, 0);
+ --mNumPendingDecodes;
+
+ if (mFlushing) {
+ completeFlushIfPossible();
+ break;
+ }
+ }
+
notify->post();
-
- ++mNumOutstandingOutputBuffers;
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 403029a..e99c24a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -198,6 +198,21 @@
mFlushingAudio = NONE;
mFlushingVideo = NONE;
}
+ } else if (what == ACodec::kWhatOutputFormatChanged) {
+ CHECK(audio);
+
+ int32_t numChannels;
+ CHECK(codecRequest->findInt32("channel-count", &numChannels));
+
+ int32_t sampleRate;
+ CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
+
+ LOGI("Audio output format changed to %d Hz, %d channels",
+ sampleRate, numChannels);
+
+ mAudioSink->close();
+ CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+ mAudioSink->start();
} else {
CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
@@ -365,18 +380,6 @@
const sp<MetaData> &meta = source->getFormat();
(*decoder)->configure(meta);
- if (audio) {
- int32_t sampleRate;
- int32_t channelCount;
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
- CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
-
- channelCount = 2; // XXX
-
- CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
- mAudioSink->start();
- }
-
return OK;
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 7613d04..d1525cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -33,8 +33,6 @@
#include "UDPPusher.h"
#include <binder/IPCThreadState.h>
-#include <binder/MemoryDealer.h>
-#include <media/IStreamSource.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AudioPlayer.h>
@@ -161,245 +159,6 @@
////////////////////////////////////////////////////////////////////////////////
-struct QueueDataSource;
-
-struct QueueListener : public BnStreamListener {
- QueueListener(QueueDataSource *owner)
- : mOwner(owner) {
- }
-
- void clearOwner();
-
- virtual void queueBuffer(size_t index, size_t size);
-
- virtual void issueCommand(
- Command cmd, bool synchronous, const sp<AMessage> &msg);
-
-private:
- Mutex mLock;
-
- QueueDataSource *mOwner;
-
- DISALLOW_EVIL_CONSTRUCTORS(QueueListener);
-};
-
-struct QueueDataSource : public DataSource {
- QueueDataSource(const sp<IStreamSource> &source);
-
- virtual status_t initCheck() const;
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
- virtual void queueBuffer(size_t index, size_t size);
-
- virtual void issueCommand(
- IStreamListener::Command cmd,
- bool synchronous,
- const sp<AMessage> &msg);
-
-protected:
- virtual ~QueueDataSource();
-
-private:
- enum {
- kNumBuffers = 16
- };
-
- struct QueueEntry {
- bool mIsCommand;
-
- IStreamListener::Command mCommand;
- sp<AMessage> mCommandMessage;
-
- size_t mIndex;
- size_t mOffset;
- size_t mSize;
- };
-
- Mutex mLock;
- Condition mCondition;
-
- sp<IStreamSource> mSource;
- sp<QueueListener> mListener;
- sp<MemoryDealer> mDealer;
- Vector<sp<IMemory> > mBuffers;
-
- List<QueueEntry> mQueue;
-
- off64_t mPosition;
- bool mEOS;
-
- DISALLOW_EVIL_CONSTRUCTORS(QueueDataSource);
-};
-
-QueueDataSource::QueueDataSource(const sp<IStreamSource> &source)
- : mSource(source),
- mPosition(0),
- mEOS(false) {
- mListener = new QueueListener(this);
- mSource->setListener(mListener);
-
- static const size_t kBufferSize = (8192 / 188) * 188;
-
- mDealer = new MemoryDealer(kNumBuffers * kBufferSize);
- for (size_t i = 0; i < kNumBuffers; ++i) {
- sp<IMemory> mem = mDealer->allocate(kBufferSize);
- CHECK(mem != NULL);
-
- mBuffers.push(mem);
- }
- mSource->setBuffers(mBuffers);
-
- for (size_t i = 0; i < kNumBuffers; ++i) {
- mSource->onBufferAvailable(i);
- }
-}
-
-QueueDataSource::~QueueDataSource() {
- Mutex::Autolock autoLock(mLock);
-
- mListener->clearOwner();
-}
-
-status_t QueueDataSource::initCheck() const {
- return OK;
-}
-
-ssize_t QueueDataSource::readAt(off64_t offset, void *data, size_t size) {
- if (offset != mPosition) {
- return -EPIPE;
- }
-
- Mutex::Autolock autoLock(mLock);
-
- if (mEOS) {
- return ERROR_END_OF_STREAM;
- }
-
- size_t sizeDone = 0;
-
- while (sizeDone < size) {
- while (mQueue.empty()) {
- mCondition.wait(mLock);
- }
-
- QueueEntry &entry = *mQueue.begin();
-
- if (entry.mIsCommand) {
- switch (entry.mCommand) {
- case IStreamListener::EOS:
- {
- mEOS = true;
-
- if (sizeDone > 0) {
- offset += sizeDone;
- return sizeDone;
- } else {
- return ERROR_END_OF_STREAM;
- }
- break;
- }
-
- case IStreamListener::DISCONTINUITY:
- {
- CHECK_EQ(size, 188u);
- CHECK_EQ(sizeDone, 0u);
-
- memset(data, 0, size);
- sizeDone = size;
- break;
- }
-
- default:
- break;
- }
-
- mQueue.erase(mQueue.begin());
- continue;
- }
-
- size_t copy = size - sizeDone;
- if (copy > entry.mSize) {
- copy = entry.mSize;
- }
-
- memcpy((uint8_t *)data + sizeDone,
- (const uint8_t *)mBuffers.itemAt(entry.mIndex)->pointer()
- + entry.mOffset,
- copy);
-
- entry.mSize -= copy;
- entry.mOffset += copy;
- sizeDone += copy;
-
- if (entry.mSize == 0) {
- mSource->onBufferAvailable(entry.mIndex);
- mQueue.erase(mQueue.begin());
- }
- }
-
- mPosition += sizeDone;
-
- return sizeDone;
-}
-
-void QueueDataSource::queueBuffer(size_t index, size_t size) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK_LT(index, mBuffers.size());
- CHECK_LE(size, mBuffers.itemAt(index)->size());
-
- QueueEntry entry;
- entry.mIsCommand = false;
- entry.mIndex = index;
- entry.mSize = size;
- entry.mOffset = 0;
-
- mQueue.push_back(entry);
- mCondition.signal();
-}
-
-void QueueDataSource::issueCommand(
- IStreamListener::Command cmd,
- bool synchronous,
- const sp<AMessage> &msg) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(!synchronous);
-
- QueueEntry entry;
- entry.mIsCommand = true;
- entry.mCommand = cmd;
- entry.mCommandMessage = msg;
- mQueue.push_back(entry);
-
- mCondition.signal();
-}
-
-void QueueListener::clearOwner() {
- Mutex::Autolock autoLock(mLock);
- mOwner = NULL;
-}
-
-void QueueListener::queueBuffer(size_t index, size_t size) {
- Mutex::Autolock autoLock(mLock);
- if (mOwner == NULL) {
- return;
- }
- mOwner->queueBuffer(index, size);
-}
-
-void QueueListener::issueCommand(
- Command cmd, bool synchronous, const sp<AMessage> &msg) {
- Mutex::Autolock autoLock(mLock);
- if (mOwner == NULL) {
- return;
- }
- mOwner->issueCommand(cmd, synchronous, msg);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
AwesomePlayer::AwesomePlayer()
: mQueueStarted(false),
mTimeSource(NULL),
@@ -511,14 +270,7 @@
}
status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) {
- Mutex::Autolock autoLock(mLock);
-
- reset_l();
-
- sp<DataSource> dataSource = new QueueDataSource(source);
- sp<MediaExtractor> extractor = new MPEG2TSExtractor(dataSource);
-
- return setDataSource_l(extractor);
+ return INVALID_OPERATION;
}
status_t AwesomePlayer::setDataSource_l(
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d3c7445..dfc9b5a 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -959,6 +959,13 @@
uint16_t width = U16_AT(&buffer[6 + 18]);
uint16_t height = U16_AT(&buffer[6 + 20]);
+ // The video sample is not stand-compliant if it has invalid dimension.
+ // Use some default width and height value, and
+ // let the decoder figure out the actual width and height (and thus
+ // be prepared for INFO_FOMRAT_CHANGED event).
+ if (width == 0) width = 352;
+ if (height == 0) height = 288;
+
// printf("*** coding='%s' width=%d height=%d\n",
// chunk, width, height);
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 4ce7265..653c85e 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -158,6 +158,9 @@
request.append(" HTTP/1.1\r\n");
request.append("Host: ");
request.append(mHost);
+ if (mPort != 80) {
+ request.append(StringPrintf(":%u", mPort).c_str());
+ }
request.append("\r\n");
if (offset != 0) {
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 7da9cb8..0e40acc 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -224,13 +224,22 @@
}
case kTypeObject:
- case kTypeMessage:
{
to->u.refValue = from->u.refValue;
to->u.refValue->incStrong(msg.get());
break;
}
+ case kTypeMessage:
+ {
+ sp<AMessage> copy =
+ static_cast<AMessage *>(from->u.refValue)->dup();
+
+ to->u.refValue = copy.get();
+ to->u.refValue->incStrong(msg.get());
+ break;
+ }
+
default:
{
to->u = from->u;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 62567be..30ac404 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -349,7 +349,11 @@
bool firstTime = (mPlaylist == NULL);
mPlaylist = fetchPlaylist(url.c_str());
- CHECK(mPlaylist != NULL);
+ if (mPlaylist == NULL) {
+ LOGE("failed to load playlist at url '%s'", url.c_str());
+ mDataSource->queueEOS(ERROR_IO);
+ return;
+ }
if (firstTime) {
Mutex::Autolock autoLock(mLock);
@@ -446,7 +450,11 @@
sp<ABuffer> buffer;
status_t err = fetchFile(uri.c_str(), &buffer);
- CHECK_EQ(err, (status_t)OK);
+ if (err != OK) {
+ LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+ mDataSource->queueEOS(err);
+ return;
+ }
CHECK_EQ((status_t)OK,
decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer));
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index d4a29c0..38a7cc5 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "M3UParser"
+#include <utils/Log.h>
+
#include "include/M3UParser.h"
#include <media/stagefright/foundation/AMessage.h>
@@ -88,6 +92,9 @@
if (!strncasecmp("http://", url, 7)) {
// "url" is already an absolute URL, ignore base URL.
out->setTo(url);
+
+ LOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
+
return true;
}
@@ -108,6 +115,8 @@
out->append(url);
}
+ LOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
+
return true;
}
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png
new file mode 100644
index 0000000..f865e7a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_small.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_small.png
new file mode 100644
index 0000000..04588bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_small.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 8a01a12..0eaf08e 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -15,266 +15,126 @@
* limitations under the License.
-->
-<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<!-- TabletStatusBarView extends FrameLayout -->
<com.android.systemui.statusbar.tablet.TabletStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:background="@drawable/status_bar_background"
>
- <RelativeLayout
- android:id="@+id/bar_contents"
+
+ <FrameLayout
+ android:id="@+id/bar_contents_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
-
- <!-- notification icons & panel access -->
- <LinearLayout
- android:id="@+id/notificationArea"
- android:layout_width="wrap_content"
+ <RelativeLayout
+ android:id="@+id/bar_contents"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_alignParentRight="true"
- android:orientation="horizontal"
+ android:animateLayoutChanges="true"
>
- <com.android.systemui.statusbar.tablet.InputMethodButton
- android:id="@+id/imeSwitchButton"
+
+ <!-- notification icons & panel access -->
+ <include layout="@layout/status_bar_notification_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_marginLeft="8dip"
- android:src="@drawable/ic_sysbar_ime_default"
- android:visibility="gone"
+ android:layout_alignParentRight="true"
/>
- <com.android.systemui.statusbar.tablet.NotificationIconArea
- android:id="@+id/notificationIcons"
+
+ <!-- navigation controls -->
+ <LinearLayout
+ android:id="@+id/navigationArea"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
android:orientation="horizontal"
>
- <view
- class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
- android:id="@+id/icons"
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/status_bar_icon_size"
- android:layout_gravity="top"
- android:layout_marginTop="5dp"
- android:layout_marginLeft="8dp"
+
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+ android:layout_width="80dip"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_back"
+ systemui:keyCode="4"
/>
- </com.android.systemui.statusbar.tablet.NotificationIconArea>
-
- <LinearLayout
- android:id="@+id/notificationTrigger"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- >
- <com.android.systemui.statusbar.tablet.HoloClock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+ android:layout_width="80dip"
android:layout_height="match_parent"
- >
- <TextView android:id="@+id/time_bg"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:textSize="72dip"
- android:textColor="#1f1f1f" />
- <TextView android:id="@+id/time_fg"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:textSize="72dip"
- android:textColor="#2e2e2e" />
- </com.android.systemui.statusbar.tablet.HoloClock>
-
- <TextView
- android:id="@+id/network_text"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginTop="12dp"
- android:layout_marginRight="6dip"
- android:layout_marginLeft="6dip"
- android:gravity="center"
- android:singleLine="true"
- android:visibility="gone"
- android:textSize="14dip"
- android:textColor="#606060"
+ android:src="@drawable/ic_sysbar_home"
+ systemui:keyCode="3"
/>
-
- <LinearLayout
- android:id="@+id/signal_battery_cluster"
- android:layout_width="wrap_content"
+ <ImageView android:id="@+id/recent_apps"
+ android:layout_width="80dip"
android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="center"
- >
- <FrameLayout
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="top"
- android:layout_marginTop="19dp"
- android:layout_marginRight="4dp"
- >
- <ImageView
- android:id="@+id/network_signal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- />
- <ImageView
- android:id="@+id/network_type"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- />
- </FrameLayout>
- <ImageView
- android:id="@+id/battery"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="top"
- android:layout_marginTop="19dp"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
- />
- </LinearLayout>
+ android:src="@drawable/ic_sysbar_recent"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+ android:layout_width="80dip"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_menu"
+ systemui:keyCode="82"
+ android:visibility="invisible"
+ />
</LinearLayout>
- </LinearLayout>
- <!-- navigation controls -->
- <LinearLayout
- android:id="@+id/navigationArea"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentLeft="true"
- android:orientation="horizontal"
- >
-
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
- android:layout_width="96dip"
+ <!-- fake space bar zone -->
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/fake_space_bar"
android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:src="@drawable/ic_sysbar_back"
- systemui:keyCode="4"
- />
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:src="@drawable/ic_sysbar_home"
- systemui:keyCode="3"
- />
- <ImageButton android:id="@+id/recent_apps"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:src="@drawable/ic_sysbar_recent"
- android:background="@null"
- android:paddingLeft="18dip"
- android:clickable="true"
- android:paddingRight="18dip"
- />
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:src="@drawable/ic_sysbar_menu"
- systemui:keyCode="82"
- android:visibility="invisible"
- />
- <com.android.systemui.statusbar.tablet.ShirtPocket
- android:id="@+id/pocket"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:animateLayoutChanges="true"
- android:clickable="true"
- android:descendantFocusability="blocksDescendants"
- android:gravity="center"
- >
- <ImageView
- android:id="@+id/pocket_icon"
- android:src="@drawable/ic_sysbar_pocket"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_width="96dip"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:visibility="gone"
- />
- </com.android.systemui.statusbar.tablet.ShirtPocket>
- </LinearLayout>
-
- <!-- lights out mode: "shadow" views -->
- <RelativeLayout
- android:id="@+id/shadows"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
- <ImageView
- android:id="@+id/notification_shadow"
- android:layout_width="176dip"
- android:layout_height="match_parent"
- android:paddingRight="48dip"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
+ android:layout_width="match_parent"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:layout_toRightOf="@+id/navigationArea"
+ android:layout_toLeftOf="@+id/notificationArea"
android:visibility="gone"
- android:scaleType="fitXY"
- />
-
- <ImageView
- android:id="@+id/back_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/home_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/back_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/recent_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/home_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/menu_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/recent_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
+ systemui:keyCode="62"
/>
</RelativeLayout>
+ </FrameLayout>
- <!-- fake space bar zone -->
- <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/fake_space_bar"
- android:layout_height="match_parent"
+ <FrameLayout
+ android:id="@+id/bar_shadow_holder"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <!-- lights out shade -->
+ <RelativeLayout
+ android:id="@+id/bar_shadow"
android:layout_width="match_parent"
- android:paddingLeft="8dip"
- android:paddingRight="8dip"
- android:layout_toRightOf="@+id/navigationArea"
- android:layout_toLeftOf="@+id/notificationArea"
+ android:layout_height="match_parent"
+ android:background="#FF000000"
android:visibility="gone"
- systemui:keyCode="62"
- />
- </RelativeLayout>
+ >
+ <ImageView
+ android:id="@+id/dot0"
+ android:layout_width="80dip"
+ android:layout_height="48dip"
+ android:src="@drawable/ic_sysbar_lights_out_dot_small"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ />
+ <ImageView
+ android:id="@+id/dot1"
+ android:layout_width="80dip"
+ android:layout_height="48dip"
+ android:src="@drawable/ic_sysbar_lights_out_dot_large"
+ android:layout_toRightOf="@+id/dot0"
+ android:layout_alignParentBottom="true"
+ />
+ <ImageView
+ android:id="@+id/dot2"
+ android:layout_width="80dip"
+ android:layout_height="48dip"
+ android:src="@drawable/ic_sysbar_lights_out_dot_small"
+ android:layout_toRightOf="@+id/dot1"
+ android:layout_alignParentBottom="true"
+ />
+ <ImageView
+ android:id="@+id/dot3"
+ android:layout_width="80dip"
+ android:layout_height="48dip"
+ android:src="@drawable/ic_sysbar_lights_out_dot_small"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ />
+ </RelativeLayout>
+ </FrameLayout>
</com.android.systemui.statusbar.tablet.TabletStatusBarView>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml
new file mode 100644
index 0000000..df60a26
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<!-- notification icons & panel access -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:id="@+id/notificationArea"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:orientation="horizontal"
+ >
+ <com.android.systemui.statusbar.tablet.InputMethodButton
+ android:id="@+id/imeSwitchButton"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/ic_sysbar_ime_default"
+ android:visibility="gone"
+ />
+ <com.android.systemui.statusbar.tablet.NotificationIconArea
+ android:id="@+id/notificationIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+ <view
+ class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
+ android:id="@+id/icons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:paddingTop="5dp"
+ android:layout_marginLeft="8dp"
+ />
+ </com.android.systemui.statusbar.tablet.NotificationIconArea>
+
+ <LinearLayout
+ android:id="@+id/notificationTrigger"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ >
+ <com.android.systemui.statusbar.tablet.HoloClock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ >
+ <TextView android:id="@+id/time_bg"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:textSize="72dip"
+ android:textColor="#1f1f1f" />
+ <TextView android:id="@+id/time_fg"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:textSize="72dip"
+ android:textColor="#2e2e2e" />
+ </com.android.systemui.statusbar.tablet.HoloClock>
+
+ <TextView
+ android:id="@+id/network_text"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginTop="12dp"
+ android:layout_marginRight="6dip"
+ android:layout_marginLeft="6dip"
+ android:gravity="center"
+ android:singleLine="true"
+ android:visibility="gone"
+ android:textSize="14dip"
+ android:textColor="#606060"
+ />
+
+ <LinearLayout
+ android:id="@+id/signal_battery_cluster"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <FrameLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="top"
+ android:layout_marginTop="19dp"
+ android:layout_marginRight="4dp"
+ >
+ <ImageView
+ android:id="@+id/network_signal"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
+ <ImageView
+ android:id="@+id/network_type"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
+ </FrameLayout>
+ <ImageView
+ android:id="@+id/battery"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="top"
+ android:layout_marginTop="19dp"
+ android:layout_marginLeft="2dp"
+ android:layout_marginRight="2dp"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
index 2272e34..d3fe2e7 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
@@ -163,7 +163,7 @@
android:orientation="vertical"
>
<ScrollView
- android:id="@+id/notificationScroller"
+ android:id="@+id/notification_scroller"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="1"
@@ -184,6 +184,7 @@
</com.android.systemui.statusbar.tablet.NotificationLinearLayout>
</ScrollView>
<ImageView
+ android:id="@+id/notification_glow"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_panel_bottom_offset"
android:layout_marginLeft="16dp"
diff --git a/packages/SystemUI/res/values-xlarge-port/dimens.xml b/packages/SystemUI/res/values-xlarge-port/dimens.xml
new file mode 100644
index 0000000..56effa3
--- /dev/null
+++ b/packages/SystemUI/res/values-xlarge-port/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- gap on either side of status bar notification icons -->
+ <dimen name="status_bar_icon_padding">2dp</dimen>
+</resources>
+
+
+
diff --git a/packages/SystemUI/res/values-xlarge/dimens.xml b/packages/SystemUI/res/values-xlarge/dimens.xml
index 5ae3982..b9f5837 100644
--- a/packages/SystemUI/res/values-xlarge/dimens.xml
+++ b/packages/SystemUI/res/values-xlarge/dimens.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (c) 2006, The Android Open Source Project
+ * Copyright (c) 2010, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
<dimen name="notification_ticker_width">360dp</dimen>
<!-- Status bar panel bottom offset (height of status bar - overlap) -->
<dimen name="status_bar_panel_bottom_offset">36dp</dimen>
+ <!-- gap on either side of status bar notification icons -->
+ <dimen name="status_bar_icon_padding">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 53fe2ff..2110483c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -20,11 +20,15 @@
import java.io.PrintWriter;
import android.content.Context;
+import android.content.res.Configuration;
public abstract class SystemUI {
public Context mContext;
public abstract void start();
+
+ protected void onConfigurationChanged(Configuration newConfig) {
+ }
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 65990ad..870acd3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
import android.util.Slog;
@@ -79,6 +80,13 @@
}
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ for (SystemUI ui: mServices) {
+ ui.onConfigurationChanged(newConfig);
+ }
+ }
+
/**
* Nobody binds to us.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 82c1d17..759c17c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -39,13 +39,15 @@
public class NotificationPanel extends LinearLayout implements StatusBarPanel,
View.OnClickListener {
- static final String TAG = "NotificationPanel";
+ static final String TAG = "Tablet/NotificationPanel";
+ static final boolean DEBUG = false;
boolean mShowing;
View mTitleArea;
View mSettingsButton;
View mNotificationButton;
View mNotificationScroller;
+ View mNotificationGlow;
ViewGroup mContentFrame;
Rect mContentArea;
View mSettingsView;
@@ -85,7 +87,8 @@
mNotificationButton = (ImageView)findViewById(R.id.notification_button);
mNotificationButton.setOnClickListener(this);
- mNotificationScroller = findViewById(R.id.notificationScroller);
+ mNotificationScroller = findViewById(R.id.notification_scroller);
+ mNotificationGlow = findViewById(R.id.notification_glow);
mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
}
@@ -218,10 +221,11 @@
void addSettingsView() {
LayoutInflater infl = LayoutInflater.from(getContext());
mSettingsView = infl.inflate(R.layout.status_bar_settings_view, mContentFrame, false);
- mContentFrame.addView(mSettingsView);
+ mContentFrame.addView(mSettingsView, mContentFrame.indexOfChild(mNotificationGlow));
}
private class Choreographer implements Animator.AnimatorListener {
+ boolean mVisible;
int mBgAlpha;
ValueAnimator mBgAnim;
int mPanelHeight;
@@ -245,14 +249,16 @@
}
void startAnimation(boolean visible) {
- if (mBgAnim == null) {
+ if (DEBUG) Slog.d(TAG, "startAnimation(visible=" + visible + ")");
+ if (mBgAnim != null && mVisible != visible) {
+ mBgAnim.reverse();
+ mPositionAnim.reverse();
+ } else {
createAnimation(visible);
mBgAnim.start();
mPositionAnim.start();
- } else {
- mBgAnim.reverse();
- mPositionAnim.reverse();
}
+ mVisible = visible;
}
void jumpTo(boolean visible) {
@@ -289,22 +295,32 @@
mGlowDrawable.setAlpha((int)(255 * alpha));
if (false) {
- Slog.d(TAG, "mPanelBottom=" + mPanelBottom + "translationY=" + translationY
+ Slog.d(TAG, "mPanelBottom=" + mPanelBottom + " translationY=" + translationY
+ " alpha=" + alpha + " glowY=" + glowY);
}
}
public void setPanelHeight(int h) {
mPanelHeight = h;
- setPanelBottom(mPanelBottom);
+ if (mPanelBottom == 0) {
+ // fully closed, no animation necessary
+ setPanelBottom(0);
+ } else if (mVisible) {
+ if (DEBUG) {
+ Slog.d(TAG, "panelHeight not zero but trying to open; scheduling an anim to open fully");
+ }
+ startAnimation(true);
+ }
}
public void onAnimationCancel(Animator animation) {
- //Slog.d(TAG, "onAnimationCancel mBgAlpha=" + mBgAlpha);
+ if (DEBUG) Slog.d(TAG, "onAnimationCancel mBgAlpha=" + mBgAlpha);
+ // force this to zero so we close the window
+ mBgAlpha = 0;
}
public void onAnimationEnd(Animator animation) {
- //Slog.d(TAG, "onAnimationEnd mBgAlpha=" + mBgAlpha);
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd mBgAlpha=" + mBgAlpha);
if (mBgAlpha == 0) {
setVisibility(View.GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
index 698f5af..14f2587 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
@@ -39,21 +39,19 @@
import com.android.systemui.R;
-public class ShirtPocket extends FrameLayout {
+public class ShirtPocket extends ImageView {
private static final boolean DEBUG = false;
private static final String TAG = "StatusBar/ShirtPocket";
private ClipData mClipping = null;
private View mWindow = null;
- private ImageView mIcon;
private ImageView mPreviewIcon;
private TextView mDescription;
private TextView mAltText;
public ShirtPocket(Context context, AttributeSet attrs) {
super(context, attrs);
- setupWindow();
}
// TODO: "pin area" panel, dragging things out
@@ -63,8 +61,7 @@
// Drag API notes: we must be visible to receive drag events
setVisibility(View.VISIBLE);
- mIcon = (ImageView) findViewById(R.id.pocket_icon);
- refreshStatusIcon();
+ refresh();
setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
@@ -76,19 +73,16 @@
});
}
- private void refreshStatusIcon() {
+ private void refresh() {
setClickable(mClipping != null);
- mIcon.setImageResource(mClipping == null
- ? R.drawable.ic_sysbar_pocket_hidden
- : R.drawable.ic_sysbar_pocket_holding);
- mIcon.setVisibility(mClipping == null ? View.GONE : View.VISIBLE);
+ // XXX: TODO
}
private void showWindow() {
getHandler().post(new Runnable() {
public void run() {
mWindow.setVisibility(View.VISIBLE);
- refreshStatusIcon();
+ refresh();
}
});
}
@@ -97,7 +91,7 @@
getHandler().post(new Runnable() {
public void run() {
mWindow.setVisibility(View.GONE);
- refreshStatusIcon();
+ refresh();
}
});
}
@@ -106,7 +100,7 @@
getHandler().postDelayed(new Runnable() {
public void run() {
mWindow.setVisibility(View.GONE);
- refreshStatusIcon();
+ refresh();
}
},
250);
@@ -183,65 +177,27 @@
}
};
- private void setupWindow() {
- mWindow = View.inflate(getContext(), R.layout.status_bar_pocket_panel, null);
-
- mPreviewIcon = (ImageView) mWindow.findViewById(R.id.icon);
- mDescription = (TextView) mWindow.findViewById(R.id.description);
- mAltText = (TextView) mWindow.findViewById(R.id.alt);
-
- mWindow.setVisibility(View.GONE);
- mWindow.setOnTouchListener(mWindowTouchListener);
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- 400,
- 250,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
-// int pos[] = new int[2];
-// getLocationOnScreen(pos);
-// lp.x = pos[1];
-// lp.y = 0;
- lp.setTitle("ShirtPocket");
- lp.windowAnimations = R.style.Animation_ShirtPocketPanel;
-
- WindowManagerImpl.getDefault().addView(mWindow, lp);
-
- }
-
public boolean onDragEvent(DragEvent event) {
if (DEBUG) Slog.d(TAG, "onDragEvent: " + event);
- if (mIcon != null) {
- switch (event.getAction()) {
- // We want to appear whenever a potential drag takes off from anywhere in the UI.
- case DragEvent.ACTION_DRAG_STARTED:
- mIcon.setImageResource(mClipping == null
- ? R.drawable.ic_sysbar_pocket
- : R.drawable.ic_sysbar_pocket_holding);
- mIcon.setVisibility(View.VISIBLE);
- break;
- case DragEvent.ACTION_DRAG_ENTERED:
- if (DEBUG) Slog.d(TAG, "entered!");
- mIcon.setImageResource(R.drawable.ic_sysbar_pocket_drag);
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- if (DEBUG) Slog.d(TAG, "exited!");
- mIcon.setImageResource(mClipping == null
- ? R.drawable.ic_sysbar_pocket
- : R.drawable.ic_sysbar_pocket_holding);
- break;
- case DragEvent.ACTION_DROP:
- if (DEBUG) Slog.d(TAG, "dropped!");
- stash(event.getClipData());
- refreshStatusIcon();
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- refreshStatusIcon();
- break;
- }
+ switch (event.getAction()) {
+ // We want to appear whenever a potential drag takes off from anywhere in the UI.
+ case DragEvent.ACTION_DRAG_STARTED:
+ // XXX: TODO
+ break;
+ case DragEvent.ACTION_DRAG_ENTERED:
+ if (DEBUG) Slog.d(TAG, "entered!");
+ // XXX: TODO
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ if (DEBUG) Slog.d(TAG, "exited!");
+ setVisibility(mClipping == null ? View.GONE : View.VISIBLE);
+ break;
+ case DragEvent.ACTION_DROP:
+ if (DEBUG) Slog.d(TAG, "dropped!");
+ stash(event.getClipData());
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ break;
}
return true; // we want everything, thank you
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 7a7976a..3201f8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -31,6 +31,7 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -77,15 +78,16 @@
public static final boolean DEBUG = false;
public static final String TAG = "TabletStatusBar";
+ public static final int MAX_NOTIFICATION_ICONS = 5;
+
public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
public static final int MSG_OPEN_RECENTS_PANEL = 1020;
public static final int MSG_CLOSE_RECENTS_PANEL = 1021;
- public static final int MSG_HIDE_SHADOWS = 1030;
- public static final int MSG_SHOW_SHADOWS = 1031;
- public static final int MSG_RESTORE_SHADOWS = 1032;
+ public static final int MSG_SHOW_CHROME = 1030;
+ public static final int MSG_HIDE_CHROME = 1031;
// Fitts' Law assistance for LatinIME; TODO: replace with a more general approach
private static final boolean FAKE_SPACE_BAR = true;
@@ -95,7 +97,9 @@
public static final int LIGHTS_ON_DELAY = 5000;
- int mIconSize;
+ int mBarHeight = -1;
+ int mIconSize = -1;
+ int mIconHPadding = -1;
H mHandler = new H();
@@ -134,9 +138,8 @@
View mBarContents;
- // lights out support
- View mBackShadow, mHomeShadow, mRecentShadow, mMenuShadow, mNotificationShadow;
- ShadowController mShadowController;
+ // hide system chrome ("lights out") support
+ View mShadow;
NotificationIconArea.IconLayout mIconLayout;
@@ -151,13 +154,11 @@
boolean mNotificationsOn = true;
private RecentAppsPanel mRecentsPanel;
+ public Context getContext() { return mContext; }
+
protected void addPanelWindows() {
final Context context = mContext;
- final Resources res = context.getResources();
- final int barHeight= res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
-
// Notification Panel
mNotificationPanel = (NotificationPanel)View.inflate(context,
R.layout.status_bar_notification_panel, null);
@@ -265,14 +266,37 @@
super.start(); // will add the main bar view
}
+ @Override
+ protected void onConfigurationChanged (Configuration newConfig) {
+ loadDimens();
+ }
+
+ protected void loadDimens() {
+ final Resources res = mContext.getResources();
+
+ mBarHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+
+ int newIconSize = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_icon_size);
+ int newIconHPadding = res.getDimensionPixelSize(
+ R.dimen.status_bar_icon_padding);
+
+ if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
+// Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
+ mIconHPadding = newIconHPadding;
+ mIconSize = newIconSize;
+ reloadAllNotificationIcons(); // reload the tray
+ }
+ }
+
protected View makeStatusBarView() {
final Context context = mContext;
- final Resources res = context.getResources();
mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
- mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+ loadDimens();
final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
context, R.layout.status_bar, null);
@@ -300,7 +324,7 @@
mNotificationPeekTapDuration = vc.getTapTimeout();
mNotificationFlingVelocity = 300; // px/s
- mTicker = new TabletTicker(context);
+ mTicker = new TabletTicker(this);
// The icons
mBatteryController = new BatteryController(mContext);
@@ -317,7 +341,6 @@
mHomeButton = mNavigationArea.findViewById(R.id.home);
mMenuButton = mNavigationArea.findViewById(R.id.menu);
mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
- Slog.d(TAG, "rec=" + mRecentButton + ", listener=" + mOnClickListener);
mRecentButton.setOnClickListener(mOnClickListener);
// The bar contents buttons
@@ -327,22 +350,51 @@
mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar);
// "shadows" of the status bar features, for lights-out mode
- mBackShadow = sb.findViewById(R.id.back_shadow);
- mHomeShadow = sb.findViewById(R.id.home_shadow);
- mRecentShadow = sb.findViewById(R.id.recent_shadow);
- mMenuShadow = sb.findViewById(R.id.menu_shadow);
- mNotificationShadow = sb.findViewById(R.id.notification_shadow);
+ mShadow = sb.findViewById(R.id.bar_shadow);
+ mShadow.setOnTouchListener(
+ new View.OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadow.setVisibility(View.GONE);
+ mBarContents.setVisibility(View.VISIBLE);
+ }
+ return false;
+ }
+ });
- mShadowController = new ShadowController(false);
- mShadowController.add(mBackButton, mBackShadow);
- mShadowController.add(mHomeButton, mHomeShadow);
- mShadowController.add(mRecentButton, mRecentShadow);
- mShadowController.add(mMenuButton, mMenuShadow);
- mShadowController.add(mNotificationArea, mNotificationShadow);
+ // tuning parameters
+ final int LIGHTS_GOING_OUT_SYSBAR_DURATION = 600;
+ final int LIGHTS_GOING_OUT_SHADOW_DURATION = 1000;
+ final int LIGHTS_GOING_OUT_SHADOW_DELAY = 500;
+
+ final int LIGHTS_COMING_UP_SYSBAR_DURATION = 200;
+// final int LIGHTS_COMING_UP_SYSBAR_DELAY = 50;
+ final int LIGHTS_COMING_UP_SHADOW_DURATION = 0;
+
+ LayoutTransition xition = new LayoutTransition();
+ xition.setAnimator(LayoutTransition.APPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f));
+ xition.setDuration(LayoutTransition.APPEARING, LIGHTS_COMING_UP_SYSBAR_DURATION);
+ xition.setStartDelay(LayoutTransition.APPEARING, 0);
+ xition.setAnimator(LayoutTransition.DISAPPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
+ xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_GOING_OUT_SYSBAR_DURATION);
+ xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
+ ((ViewGroup)sb.findViewById(R.id.bar_contents_holder)).setLayoutTransition(xition);
+
+ xition = new LayoutTransition();
+ xition.setAnimator(LayoutTransition.APPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
+ xition.setDuration(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DURATION);
+ xition.setStartDelay(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DELAY);
+ xition.setAnimator(LayoutTransition.DISAPPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
+ xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_COMING_UP_SHADOW_DURATION);
+ xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
+ ((ViewGroup)sb.findViewById(R.id.bar_shadow_holder)).setLayoutTransition(xition);
// set the initial view visibility
setAreThereNotifications();
- refreshNotificationTrigger();
// Add the windows
addPanelWindows();
@@ -376,7 +428,7 @@
final int peekIndex = m.arg1;
if (peekIndex < N) {
- Slog.d(TAG, "loading peek: " + peekIndex);
+ //Slog.d(TAG, "loading peek: " + peekIndex);
NotificationData.Entry entry = mNotns.get(N-1-peekIndex);
NotificationData.Entry copy = new NotificationData.Entry(
entry.key,
@@ -420,8 +472,7 @@
if (!mNotificationPanel.isShowing()) {
mNotificationPeekWindow.setVisibility(View.GONE);
mNotificationPanel.show(true, true);
- // synchronize with current shadow state
- mShadowController.hideElement(mNotificationArea);
+ mNotificationArea.setVisibility(View.GONE);
mTicker.halt();
}
break;
@@ -429,8 +480,7 @@
if (DEBUG) Slog.d(TAG, "closing notifications panel");
if (mNotificationPanel.isShowing()) {
mNotificationPanel.show(false, true);
- // synchronize with current shadow state
- mShadowController.showElement(mNotificationArea);
+ mNotificationArea.setVisibility(View.VISIBLE);
}
break;
case MSG_OPEN_RECENTS_PANEL:
@@ -441,40 +491,21 @@
if (DEBUG) Slog.d(TAG, "closing recents panel");
if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.GONE);
break;
- case MSG_HIDE_SHADOWS:
+ case MSG_SHOW_CHROME:
if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)");
- mShadowController.hideAllShadows();
+ mBarContents.setVisibility(View.VISIBLE);
+ mShadow.setVisibility(View.GONE);
break;
- case MSG_SHOW_SHADOWS:
+ case MSG_HIDE_CHROME:
if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
animateCollapse();
- mShadowController.showAllShadows();
- break;
- case MSG_RESTORE_SHADOWS:
- if (DEBUG) Slog.d(TAG, "quickly re-showing shadows if appropriate");
- mShadowController.refresh();
+ mBarContents.setVisibility(View.GONE);
+ mShadow.setVisibility(View.VISIBLE);
break;
}
}
}
- public void refreshNotificationTrigger() {
- /*
- if (mNotificationTrigger == null) return;
-
- int resId;
- boolean panel = (mNotificationPanel != null && mNotificationPanel.isShowing();
- if (!mNotificationsOn) {
- resId = R.drawable.ic_sysbar_noti_dnd;
- } else if (mNotns.size() > 0) {
- resId = panel ? R.drawable.ic_sysbar_noti_avail_open : R.drawable.ic_sysbar_noti_avail;
- } else {
- resId = panel ? R.drawable.ic_sysbar_noti_none_open : R.drawable.ic_sysbar_noti_none;
- }
- //mNotificationTrigger.setImageResource(resId);
- */
- }
-
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
}
@@ -569,6 +600,14 @@
handleNotificationError(key, notification, "Couldn't update icon: " + ic);
return;
}
+
+ if (key == mNotificationPeekKey) {
+ // must update the peek window
+ Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
+ peekMsg.arg1 = mNotificationPeekIndex;
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+ mHandler.sendMessage(peekMsg);
+ }
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
@@ -622,6 +661,11 @@
Slog.d(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
showClock(show);
}
+ if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+ boolean show = (state & StatusBarManager.DISABLE_SYSTEM_INFO) == 0;
+ Slog.d(TAG, "DISABLE_SYSTEM_INFO: " + (show ? "no" : "yes"));
+ mNotificationTrigger.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
Slog.d(TAG, "DISABLE_EXPAND: yes");
@@ -632,12 +676,12 @@
if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
// synchronize with current shadow state
- mShadowController.hideElement(mNotificationIconArea);
+ mNotificationIconArea.setVisibility(View.GONE);
mTicker.halt();
} else {
Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
// synchronize with current shadow state
- mShadowController.showElement(mNotificationIconArea);
+ mNotificationIconArea.setVisibility(View.VISIBLE);
}
} else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
@@ -672,10 +716,17 @@
if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
| StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.add(key, n);
+
+ mNotificationArea.setVisibility(View.GONE);
}
}
}
+ // called by TabletTicker when it's done with all queued ticks
+ public void doneTicking() {
+ mNotificationArea.setVisibility(View.VISIBLE);
+ }
+
public void animateExpand() {
mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
@@ -693,13 +744,12 @@
public void setLightsOn(boolean on) {
// Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
// that can't handle lights-out mode.
- if (mMenuButton.getVisibility() == View.VISIBLE
- || mMenuShadow.getVisibility() == View.VISIBLE) {
+ if (mMenuButton.getVisibility() == View.VISIBLE) {
on = true;
}
- mHandler.removeMessages(MSG_SHOW_SHADOWS);
- mHandler.removeMessages(MSG_HIDE_SHADOWS);
- mHandler.sendEmptyMessage(on ? MSG_HIDE_SHADOWS : MSG_SHOW_SHADOWS);
+ mHandler.removeMessages(MSG_HIDE_CHROME);
+ mHandler.removeMessages(MSG_SHOW_CHROME);
+ mHandler.sendEmptyMessage(on ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
}
public void setMenuKeyVisible(boolean visible) {
@@ -736,19 +786,6 @@
private void setAreThereNotifications() {
final boolean hasClearable = mNotns.hasClearableItems();
-
- //Slog.d(TAG, "setAreThereNotifications hasClerable=" + hasClearable);
-
- /*
- mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
- mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
- if (ongoing || latest) {
- mNoNotificationsTitle.setVisibility(View.GONE);
- } else {
- mNoNotificationsTitle.setVisibility(View.VISIBLE);
- }
- */
}
/**
@@ -787,7 +824,6 @@
if (!mNotificationsOn) {
mNotificationsOn = true;
mIconLayout.setVisibility(View.VISIBLE); // TODO: animation
- refreshNotificationTrigger();
} else {
int msg = !mNotificationPanel.isShowing()
? MSG_OPEN_NOTIFICATION_PANEL
@@ -817,6 +853,10 @@
}
}
+ public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
+ return new NotificationClicker(intent, pkg, tag, id);
+ }
+
private class NotificationClicker implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
@@ -885,7 +925,7 @@
// Remove the icon.
// ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
// if (iconParent != null) iconParent.removeView(entry.icon);
- refreshIcons();
+ updateNotificationIcons();
return entry.notification;
}
@@ -981,18 +1021,25 @@
// Add the icon.
mNotns.add(entry);
- refreshIcons();
+ updateNotificationIcons();
return iconView;
}
- private void refreshIcons() {
+ private void reloadAllNotificationIcons() {
+ if (mIconLayout == null) return;
+ mIconLayout.removeAllViews();
+ updateNotificationIcons();
+ }
+
+ private void updateNotificationIcons() {
// XXX: need to implement a new limited linear layout class
// to avoid removing & readding everything
- final int ICON_LIMIT = 4;
+ if (mIconLayout == null) return;
+
final LinearLayout.LayoutParams params
- = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+ = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mBarHeight);
int N = mNotns.size();
@@ -1002,7 +1049,7 @@
ArrayList<View> toShow = new ArrayList<View>();
- for (int i=0; i<ICON_LIMIT; i++) {
+ for (int i=0; i<MAX_NOTIFICATION_ICONS; i++) {
if (i>=N) break;
toShow.add(mNotns.get(N-i-1).icon);
}
@@ -1021,13 +1068,13 @@
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
+ v.setPadding(mIconHPadding, 0, mIconHPadding, 0);
if (v.getParent() == null) {
- mIconLayout.addView(toShow.get(i), i, params);
+ mIconLayout.addView(v, i, params);
}
}
loadNotificationPanel();
- refreshNotificationTrigger();
}
private void loadNotificationPanel() {
@@ -1143,6 +1190,7 @@
return true;
}
+/*
public class ShadowController {
boolean mShowShadows;
Map<View, View> mShadowsForElements = new IdentityHashMap<View, View>(7);
@@ -1289,6 +1337,7 @@
button.setVisibility(mShowShadows ? View.INVISIBLE : View.VISIBLE);
}
}
+ */
public class TouchOutsideListener implements View.OnTouchListener {
private int mMsg;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index b3aed03..32f1e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -18,7 +18,9 @@
import java.util.Arrays;
+import android.animation.LayoutTransition;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -45,9 +47,14 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
-public class TabletTicker extends Handler {
+public class TabletTicker
+ extends Handler
+ implements LayoutTransition.TransitionListener {
+
private static final String TAG = "StatusBar.TabletTicker";
+ private static final boolean CLICKABLE_TICKER = true;
+
// 3 is enough to let us see most cases, but not get so far behind that it's too annoying.
private static final int QUEUE_LENGTH = 3;
@@ -66,8 +73,14 @@
private StatusBarNotification[] mQueue = new StatusBarNotification[QUEUE_LENGTH];
private int mQueuePos;
- public TabletTicker(Context context) {
- mContext = context;
+ private TabletStatusBar mBar;
+
+ private LayoutTransition mLayoutTransition;
+ private boolean mWindowShouldClose;
+
+ public TabletTicker(TabletStatusBar bar) {
+ mBar = bar;
+ mContext = bar.getContext();
}
public void add(IBinder key, StatusBarNotification notification) {
@@ -170,11 +183,7 @@
}
// if there's nothing left, close the window
- // TODO: Do this when the animation is done instead
- if (mCurrentView == null && mWindow != null) {
- WindowManagerImpl.getDefault().removeView(mWindow);
- mWindow = null;
- }
+ mWindowShouldClose = (mCurrentView == null && mWindow != null);
}
private void dequeue() {
@@ -208,11 +217,29 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+// lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
+
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.addTransitionListener(this);
+ view.setLayoutTransition(mLayoutTransition);
lp.setTitle("NotificationTicker");
view.setLayoutParams(lp);
return view;
}
+ public void startTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {}
+
+ public void endTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {
+ if (mWindowShouldClose) {
+ WindowManagerImpl.getDefault().removeView(mWindow);
+ mWindow = null;
+ mWindowShouldClose = false;
+ mBar.doneTicking();
+ }
+ }
+
private View makeTickerView(StatusBarNotification notification) {
final Notification n = notification.notification;
@@ -266,6 +293,17 @@
largeIcon.setImageBitmap(n.largeIcon);
largeIcon.setVisibility(View.VISIBLE);
}
+
+ if (CLICKABLE_TICKER) {
+ PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ group.setOnClickListener(mBar.makeClicker(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ } else {
+ group.setOnClickListener(null);
+ }
+ }
+
return group;
}
}
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
index aee01ab..d926cb1 100644
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ b/services/audioflinger/A2dpAudioInterface.cpp
@@ -260,6 +260,7 @@
if (pRate) *pRate = lRate;
mDevice = device;
+ mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
return NO_ERROR;
}
@@ -288,6 +289,7 @@
if (mStandby) {
acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
mStandby = false;
+ mLastWriteTime = systemTime();
}
status = init();
@@ -308,6 +310,15 @@
buffer = (char *)buffer + status;
}
+ // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
+ // does no spin and starve other threads.
+ // NOTE: It is likely that the A2DP headset is being disconnected
+ nsecs_t now = systemTime();
+ if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
+ LOGV("A2DP sink runs too fast");
+ usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
+ }
+ mLastWriteTime = now;
return bytes;
}
@@ -316,7 +327,7 @@
standby();
// Simulate audio output timing in case of error
- usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
+ usleep(mBufferDurationUs);
return status;
}
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
index cef1926..dbe2c6a 100644
--- a/services/audioflinger/A2dpAudioInterface.h
+++ b/services/audioflinger/A2dpAudioInterface.h
@@ -117,6 +117,8 @@
uint32_t mDevice;
bool mClosing;
bool mSuspended;
+ nsecs_t mLastWriteTime;
+ uint32_t mBufferDurationUs;
};
friend class A2dpAudioStreamOut;
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 5b9273d..855af9f 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -1605,10 +1605,8 @@
break;
case AudioSystem::FORCE_SPEAKER:
- if (!isInCall() || strategy != STRATEGY_DTMF) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- if (device) break;
- }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+ if (device) break;
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
// A2DP speaker when forcing to speaker output
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 95200fa..9c56a2a 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -99,6 +99,7 @@
static final int MSG_SHOW_IM_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
+ static final int MSG_SHOW_IM_CONFIG = 4;
static final int MSG_UNBIND_INPUT = 1000;
static final int MSG_BIND_INPUT = 1010;
@@ -120,6 +121,7 @@
// If IME doesn't support the system locale, the default subtype will be the first defined one.
private static final int DEFAULT_SUBTYPE_ID = 0;
+ private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
@@ -1313,14 +1315,14 @@
}
public void showInputMethodAndSubtypeEnablerFromClient(
- IInputMethodClient client, String topId) {
- // TODO: Handle topId for setting the top position of the list ActivityManagerNative
+ IInputMethodClient client, String inputMethodId) {
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client);
}
- mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_ENABLER);
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
+ MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
}
}
@@ -1428,7 +1430,12 @@
return true;
case MSG_SHOW_IM_SUBTYPE_ENABLER:
- showInputMethodAndSubtypeEnabler();
+ args = (HandlerCaller.SomeArgs)msg.obj;
+ showInputMethodAndSubtypeEnabler((String)args.arg1);
+ return true;
+
+ case MSG_SHOW_IM_CONFIG:
+ showConfigureInputMethods();
return true;
// ---------------------------------------------------------
@@ -1624,11 +1631,22 @@
showInputMethodMenuInternal(true);
}
- private void showInputMethodAndSubtypeEnabler() {
+ private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (!TextUtils.isEmpty(inputMethodId)) {
+ intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
+ }
+ mContext.startActivity(intent);
+ }
+
+ private void showConfigureInputMethods() {
+ Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContext.startActivity(intent);
}
@@ -1763,7 +1781,7 @@
mDialogBuilder.setPositiveButton(com.android.internal.R.string.more_item_label,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- showInputMethodAndSubtypeEnabler();
+ showConfigureInputMethods();
}
});
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 4290ce7..152605f 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -295,7 +295,7 @@
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException(
- "Unable to communicate with native daemon to interface setcfg");
+ "Unable to communicate with native daemon to interface setcfg - " + e);
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index ba7692d..26dd692 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4958,7 +4958,7 @@
}
Binder.restoreCallingIdentity(ident);
- if (frame.isEmpty()) {
+ if (frame.isEmpty() || maxLayer == 0) {
return null;
}
@@ -4990,6 +4990,12 @@
rawss = Surface.screenshot(dw, dh, 0, maxLayer);
}
+ if (rawss == null) {
+ Log.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh
+ + ") to layer " + maxLayer);
+ return null;
+ }
+
Bitmap bm = Bitmap.createBitmap(sw, sh, rawss.getConfig());
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7c4790f..825d90b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2123,6 +2123,7 @@
// invert everything, b/c glReadPixel() below will invert the FB
glViewport(0, 0, sw, sh);
+ glScissor(0, 0, sw, sh);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
@@ -2132,6 +2133,7 @@
// redraw the screen entirely...
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
+
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
@@ -2169,7 +2171,6 @@
result = NO_MEMORY;
}
}
-
glEnable(GL_SCISSOR_TEST);
glViewport(0, 0, hw_w, hw_h);
glMatrixMode(GL_PROJECTION);
diff --git a/test-runner/src/android/test/LoaderTestCase.java b/test-runner/src/android/test/LoaderTestCase.java
index 8be6590..c8564c2 100644
--- a/test-runner/src/android/test/LoaderTestCase.java
+++ b/test-runner/src/android/test/LoaderTestCase.java
@@ -64,7 +64,7 @@
// Shut the loader down
completedLoader.unregisterListener(this);
completedLoader.stopLoading();
- completedLoader.destroy();
+ completedLoader.reset();
// Store the result, unblocking the test thread
queue.add(data);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 56beada..b2d2a98 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -92,6 +92,7 @@
ignoreResultList.add("fast/encoding/percent-escaping.html"); // fails in Java HTTP stack, see http://b/issue?id=3046984
ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
ignoreResultList.add("http/tests/appcache/fallback.html"); // http://b/issue?id=2713004
+ ignoreResultList.add("http/tests/appcache/foreign-fallback.html"); // Flaky, may be due to DRT, see http://b/3285647
ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
ignoreResultList.add("http/tests/appcache/origin-quota.html"); // needs clearAllApplicationCaches(), see http://b/issue?id=2944196
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 44c7d72..563f28c 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -178,6 +178,11 @@
mStatusBarManager.disable(StatusBarManager.DISABLE_CLOCK);
}
},
+ new Test("Disable System Info") {
+ public void run() {
+ mStatusBarManager.disable(StatusBarManager.DISABLE_SYSTEM_INFO);
+ }
+ },
new Test("Disable everything in 3 sec") {
public void run() {
mHandler.postDelayed(new Runnable() {
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 4029ed0..f8fd2b7 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -177,7 +177,7 @@
*/
public Builder setPort(int port) throws IllegalArgumentException {
if ((port > 65535) || (port < 1000)) {
- throw new IllegalArgumentException("incorrect port arugment");
+ throw new IllegalArgumentException("incorrect port arugment: " + port);
}
mProfile.mPort = port;
return this;
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 30ddfb5..edf8b52 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -1333,10 +1333,12 @@
SipURI uri = (SipURI) address.getURI();
String username = uri.getUser();
if (username == null) username = ANONYMOUS;
- return new SipProfile.Builder(username, uri.getHost())
- .setPort(uri.getPort())
- .setDisplayName(address.getDisplayName())
- .build();
+ int port = uri.getPort();
+ SipProfile.Builder builder =
+ new SipProfile.Builder(username, uri.getHost())
+ .setDisplayName(address.getDisplayName());
+ if (port > 0) builder.setPort(port);
+ return builder.build();
} catch (IllegalArgumentException e) {
throw new SipException("createPeerProfile()", e);
} catch (ParseException e) {
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index f597934..e650b87 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1112,7 +1112,7 @@
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
- config.SSID = removeDoubleQuotes(value);
+ config.SSID = value;
} else {
config.SSID = null;
}