summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/accounts/AccountManagerService.java34
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ActivityManager.java21
-rw-r--r--core/java/android/app/ActivityManagerNative.java33
-rw-r--r--core/java/android/app/ActivityOptions.java194
-rw-r--r--core/java/android/app/ActivityThread.java56
-rw-r--r--core/java/android/app/ApplicationPackageManager.java61
-rw-r--r--core/java/android/app/ContextImpl.java11
-rw-r--r--core/java/android/app/IActivityManager.java8
-rw-r--r--core/java/android/app/Instrumentation.java2
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/app/Notification.java194
-rw-r--r--core/java/android/content/Context.java14
-rw-r--r--core/java/android/content/Intent.java83
-rw-r--r--core/java/android/content/SyncManager.java23
-rw-r--r--core/java/android/content/SyncQueue.java13
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl44
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java98
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java105
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java79
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java238
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java28
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java80
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java8
-rw-r--r--core/java/android/net/Downloads.java644
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl4
-rw-r--r--core/java/android/net/NetworkPolicy.java14
-rw-r--r--core/java/android/net/NetworkPolicyManager.java11
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java64
-rw-r--r--core/java/android/os/Bundle.java7
-rw-r--r--core/java/android/os/UserId.java17
-rw-r--r--core/java/android/provider/Downloads.java16
-rw-r--r--core/java/android/provider/Settings.java8
-rwxr-xr-xcore/java/android/server/BluetoothService.java28
-rw-r--r--core/java/android/view/DisplayList.java26
-rw-r--r--core/java/android/view/FocusFinder.java82
-rw-r--r--core/java/android/view/GLES20Canvas.java8
-rw-r--r--core/java/android/view/HardwareCanvas.java16
-rw-r--r--core/java/android/view/HardwareRenderer.java51
-rw-r--r--core/java/android/view/IWindowManager.aidl2
-rw-r--r--core/java/android/view/MotionEvent.java20
-rw-r--r--core/java/android/view/Surface.java18
-rw-r--r--core/java/android/view/TextureView.java2
-rw-r--r--core/java/android/view/View.java544
-rw-r--r--core/java/android/view/ViewGroup.java31
-rw-r--r--core/java/android/view/ViewRootImpl.java31
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/view/animation/Animation.java2
-rw-r--r--core/java/android/view/animation/Transformation.java10
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java13
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.java16
-rw-r--r--core/java/android/webkit/BrowserFrame.java4
-rw-r--r--core/java/android/webkit/DebugFlags.java7
-rw-r--r--core/java/android/webkit/FindListener.java32
-rw-r--r--core/java/android/webkit/HTML5Audio.java2
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java2
-rw-r--r--core/java/android/webkit/WebSettingsClassic.java37
-rw-r--r--core/java/android/webkit/WebView.java31
-rw-r--r--core/java/android/webkit/WebViewClassic.java308
-rw-r--r--core/java/android/webkit/WebViewCore.java214
-rw-r--r--core/java/android/webkit/WebViewDatabase.java6
-rw-r--r--core/java/android/webkit/WebViewProvider.java5
-rw-r--r--core/java/android/widget/AbsSeekBar.java10
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java26
-rw-r--r--core/java/android/widget/DatePicker.java37
-rw-r--r--core/java/android/widget/GridView.java100
-rw-r--r--core/java/android/widget/HorizontalScrollView.java12
-rw-r--r--core/java/android/widget/ImageView.java133
-rw-r--r--core/java/android/widget/NumberPicker.java1299
-rw-r--r--core/java/android/widget/RadioGroup.java2
-rw-r--r--core/java/android/widget/RatingBar.java4
-rw-r--r--core/java/android/widget/RemoteViews.java84
-rw-r--r--core/java/android/widget/ScrollView.java54
-rw-r--r--core/java/android/widget/TextView.java7
-rw-r--r--core/java/android/widget/TimePicker.java31
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java2
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java23
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java9
-rw-r--r--core/java/com/android/internal/util/StateMachine.java61
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java28
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java124
-rw-r--r--core/java/com/google/android/mms/util/DownloadDrmHelper.java111
-rw-r--r--core/java/com/google/android/mms/util/DrmConvertSession.java172
84 files changed, 3769 insertions, 2346 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index cf1e8f3..197c1bd 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -217,10 +217,10 @@ public class AccountManagerService
mAuthenticatorCache = authenticatorCache;
mAuthenticatorCache.setListener(this, null /* Handler */);
- UserAccounts accounts = initUser(0);
-
sThis.set(this);
+ UserAccounts accounts = initUser(0);
+
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -231,6 +231,14 @@ public class AccountManagerService
}
}, intentFilter);
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
}
private UserAccounts initUser(int userId) {
@@ -347,6 +355,28 @@ public class AccountManagerService
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId < 1) return;
+
+ UserAccounts accounts;
+ synchronized (mUsers) {
+ accounts = mUsers.get(userId);
+ mUsers.remove(userId);
+ }
+ if (accounts == null) {
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ return;
+ }
+
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ }
+ }
+
private List<UserInfo> getAllUsers() {
try {
return AppGlobals.getPackageManager().getUsers();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ea32745..b277efb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3639,7 +3639,7 @@ public class Activity extends ContextThemeWrapper
*/
public void startActivityFromChild(Activity child, Intent intent,
int requestCode) {
- startActivityFromChild(child, intent, requestCode);
+ startActivityFromChild(child, intent, requestCode, null);
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d056b17..531a695 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
@@ -816,6 +817,19 @@ public class ActivityManager {
public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002;
/**
+ * Equivalent to calling {@link #moveTaskToFront(int, int, Bundle)}
+ * with a null options argument.
+ *
+ * @param taskId The identifier of the task to be moved, as found in
+ * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+ * @param flags Additional operational flags, 0 or more of
+ * {@link #MOVE_TASK_WITH_HOME}.
+ */
+ public void moveTaskToFront(int taskId, int flags) {
+ moveTaskToFront(taskId, flags, null);
+ }
+
+ /**
* Ask that the task associated with a given task ID be moved to the
* front of the stack, so it is now visible to the user. Requires that
* the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
@@ -825,10 +839,13 @@ public class ActivityManager {
* {@link RunningTaskInfo} or {@link RecentTaskInfo}.
* @param flags Additional operational flags, 0 or more of
* {@link #MOVE_TASK_WITH_HOME}.
+ * @param options Additional options for the operation, either null or
+ * as per {@link Context#startActivity(Intent, android.os.Bundle)
+ * Context.startActivity(Intent, Bundle)}.
*/
- public void moveTaskToFront(int taskId, int flags) {
+ public void moveTaskToFront(int taskId, int flags, Bundle options) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+ ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3cc352..c402329 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -510,7 +510,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
int task = data.readInt();
int fl = data.readInt();
- moveTaskToFront(task, fl);
+ Bundle options = data.readInt() != 0
+ ? Bundle.CREATOR.createFromParcel(data) : null;
+ moveTaskToFront(task, fl, options);
reply.writeNoException();
return true;
}
@@ -1055,6 +1057,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String reason = data.readString();
+ boolean res = killProcessesBelowForeground(reason);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
case START_RUNNING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -2134,13 +2145,19 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public void moveTaskToFront(int task, int flags) throws RemoteException
+ public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(task);
data.writeInt(flags);
+ if (options != null) {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -2902,6 +2919,18 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
+ @Override
+ public boolean killProcessesBelowForeground(String reason) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(reason);
+ mRemote.transact(KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION, data, reply, 0);
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
public void startRunning(String pkg, String cls, String action,
String indata) throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 03bc338..c637df0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,13 @@
package android.app;
import android.content.Context;
+import android.graphics.Bitmap;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Message;
+import android.os.RemoteException;
+import android.view.View;
/**
* Helper class for building an options Bundle that can be used with
@@ -32,6 +38,12 @@ public class ActivityOptions {
public static final String KEY_PACKAGE_NAME = "android:packageName";
/**
+ * Type of animation that arguments specify.
+ * @hide
+ */
+ public static final String KEY_ANIM_TYPE = "android:animType";
+
+ /**
* Custom enter animation resource ID.
* @hide
*/
@@ -43,10 +55,45 @@ public class ActivityOptions {
*/
public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes";
+ /**
+ * Bitmap for thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail";
+
+ /**
+ * Start X position of thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_X = "android:animStartX";
+
+ /**
+ * Start Y position of thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_Y = "android:animStartY";
+
+ /**
+ * Callback for when animation is started.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_LISTENER = "android:animStartListener";
+
+ /** @hide */
+ public static final int ANIM_NONE = 0;
+ /** @hide */
+ public static final int ANIM_CUSTOM = 1;
+ /** @hide */
+ public static final int ANIM_THUMBNAIL = 2;
+
private String mPackageName;
- private boolean mIsCustomAnimation;
+ private int mAnimationType = ANIM_NONE;
private int mCustomEnterResId;
private int mCustomExitResId;
+ private Bitmap mThumbnail;
+ private int mStartX;
+ private int mStartY;
+ private IRemoteCallback mAnimationStartedListener;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -65,22 +112,79 @@ public class ActivityOptions {
int enterResId, int exitResId) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = context.getPackageName();
- opts.mIsCustomAnimation = true;
+ opts.mAnimationType = ANIM_CUSTOM;
opts.mCustomEnterResId = enterResId;
opts.mCustomExitResId = exitResId;
return opts;
}
+ /**
+ * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
+ * to find out when the given animation has started running.
+ */
+ public interface OnAnimationStartedListener {
+ void onAnimationStarted();
+ }
+
+ /**
+ * Create an ActivityOptions specifying an animation where a thumbnail
+ * is scaled from a given position to the new activity window that is
+ * being started.
+ *
+ * @param source The View that this thumbnail is animating from. This
+ * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+ * @param thumbnail The bitmap that will be shown as the initial thumbnail
+ * of the animation.
+ * @param startX The x starting location of the bitmap, in screen coordiantes.
+ * @param startY The y starting location of the bitmap, in screen coordinates.
+ * @param listener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ */
+ public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
+ Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
+ ActivityOptions opts = new ActivityOptions();
+ opts.mPackageName = source.getContext().getPackageName();
+ opts.mAnimationType = ANIM_THUMBNAIL;
+ opts.mThumbnail = thumbnail;
+ int[] pts = new int[2];
+ source.getLocationOnScreen(pts);
+ opts.mStartX = pts[0] + startX;
+ opts.mStartY = pts[1] + startY;
+ if (listener != null) {
+ final Handler h = source.getHandler();
+ final OnAnimationStartedListener finalListener = listener;
+ opts.mAnimationStartedListener = new IRemoteCallback.Stub() {
+ @Override public void sendResult(Bundle data) throws RemoteException {
+ h.post(new Runnable() {
+ @Override public void run() {
+ finalListener.onAnimationStarted();
+ }
+ });
+ }
+ };
+ }
+ return opts;
+ }
+
private ActivityOptions() {
}
/** @hide */
public ActivityOptions(Bundle opts) {
mPackageName = opts.getString(KEY_PACKAGE_NAME);
- if (opts.containsKey(KEY_ANIM_ENTER_RES_ID)) {
- mIsCustomAnimation = true;
+ mAnimationType = opts.getInt(KEY_ANIM_TYPE);
+ if (mAnimationType == ANIM_CUSTOM) {
mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+ } else if (mAnimationType == ANIM_THUMBNAIL) {
+ mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
+ mStartX = opts.getInt(KEY_ANIM_START_X, 0);
+ mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+ mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
+ opts.getIBinder(KEY_ANIM_START_LISTENER));
}
}
@@ -90,8 +194,8 @@ public class ActivityOptions {
}
/** @hide */
- public boolean isCustomAnimation() {
- return mIsCustomAnimation;
+ public int getAnimationType() {
+ return mAnimationType;
}
/** @hide */
@@ -104,6 +208,43 @@ public class ActivityOptions {
return mCustomExitResId;
}
+ /** @hide */
+ public Bitmap getThumbnail() {
+ return mThumbnail;
+ }
+
+ /** @hide */
+ public int getStartX() {
+ return mStartX;
+ }
+
+ /** @hide */
+ public int getStartY() {
+ return mStartY;
+ }
+
+ /** @hide */
+ public IRemoteCallback getOnAnimationStartListener() {
+ return mAnimationStartedListener;
+ }
+
+ /** @hide */
+ public void abort() {
+ if (mAnimationStartedListener != null) {
+ try {
+ mAnimationStartedListener.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /** @hide */
+ public static void abort(Bundle options) {
+ if (options != null) {
+ (new ActivityOptions(options)).abort();
+ }
+ }
+
/**
* Join the values in <var>otherOptions</var> in to this one. Any values
* defined in <var>otherOptions</var> replace those in the base options.
@@ -112,10 +253,27 @@ public class ActivityOptions {
if (otherOptions.mPackageName != null) {
mPackageName = otherOptions.mPackageName;
}
- if (otherOptions.mIsCustomAnimation) {
- mIsCustomAnimation = true;
- mCustomEnterResId = otherOptions.mCustomEnterResId;
- mCustomExitResId = otherOptions.mCustomExitResId;
+ switch (otherOptions.mAnimationType) {
+ case ANIM_CUSTOM:
+ mAnimationType = otherOptions.mAnimationType;
+ mCustomEnterResId = otherOptions.mCustomEnterResId;
+ mCustomExitResId = otherOptions.mCustomExitResId;
+ mThumbnail = null;
+ mAnimationStartedListener = null;
+ break;
+ case ANIM_THUMBNAIL:
+ mAnimationType = otherOptions.mAnimationType;
+ mThumbnail = otherOptions.mThumbnail;
+ mStartX = otherOptions.mStartX;
+ mStartY = otherOptions.mStartY;
+ if (otherOptions.mAnimationStartedListener != null) {
+ try {
+ otherOptions.mAnimationStartedListener.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ mAnimationStartedListener = otherOptions.mAnimationStartedListener;
+ break;
}
}
@@ -132,9 +290,19 @@ public class ActivityOptions {
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
- if (mIsCustomAnimation) {
- b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
- b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+ switch (mAnimationType) {
+ case ANIM_CUSTOM:
+ b.putInt(KEY_ANIM_TYPE, mAnimationType);
+ b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
+ b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+ break;
+ case ANIM_THUMBNAIL:
+ b.putInt(KEY_ANIM_TYPE, mAnimationType);
+ b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
+ b.putInt(KEY_ANIM_START_X, mStartX);
+ b.putInt(KEY_ANIM_START_Y, mStartY);
+ b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
+ != null ? mAnimationStartedListener.asBinder() : null);
}
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a3e213..ab4e73d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -404,6 +404,7 @@ public final class ActivityThread {
try {
fd.close();
} catch (IOException e) {
+ // Ignore
}
}
return;
@@ -412,6 +413,7 @@ public final class ActivityThread {
try {
profileFd.close();
} catch (IOException e) {
+ // Ignore
}
}
profileFile = file;
@@ -843,14 +845,13 @@ public final class ActivityThread {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new PrintWriter(fout);
try {
- return dumpMemInfo(pw, checkin, all, args);
+ return dumpMemInfo(pw, checkin, all);
} finally {
pw.flush();
}
}
- private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all,
- String[] args) {
+ private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -1458,17 +1459,6 @@ public final class ActivityThread {
return dm;
}
- static Configuration applyConfigCompat(Configuration config, CompatibilityInfo compat) {
- if (config == null) {
- return null;
- }
- if (compat != null && !compat.supportsScreen()) {
- config = new Configuration(config);
- compat.applyToConfiguration(config);
- }
- return config;
- }
-
private Configuration mMainThreadConfig = new Configuration();
Configuration applyConfigCompatMainThread(Configuration config, CompatibilityInfo compat) {
if (config == null) {
@@ -1586,7 +1576,7 @@ public final class ActivityThread {
ApplicationInfo ai = null;
try {
ai = getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES);
+ PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
} catch (RemoteException e) {
// Ignore
}
@@ -2509,7 +2499,7 @@ public final class ActivityThread {
return r;
}
- final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
+ static final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
if (r.mPendingRemoveWindow != null) {
r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
@@ -3437,15 +3427,12 @@ public final class ActivityThread {
= new ArrayList<ComponentCallbacks2>();
if (mActivities.size() > 0) {
- Iterator<ActivityClientRecord> it = mActivities.values().iterator();
- while (it.hasNext()) {
- ActivityClientRecord ar = it.next();
+ for (ActivityClientRecord ar : mActivities.values()) {
Activity a = ar.activity;
if (a != null) {
Configuration thisConfig = applyConfigCompatMainThread(newConfig,
ar.packageInfo.mCompatibilityInfo.getIfNeeded());
- if (!ar.activity.mFinished && (allActivities ||
- (a != null && !ar.paused))) {
+ if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
// If the activity is currently resumed, its configuration
// needs to change right now.
callbacks.add(a);
@@ -3455,24 +3442,24 @@ public final class ActivityThread {
// the activity manager may, before then, decide the
// activity needs to be destroyed to handle its new
// configuration.
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Setting activity "
- + ar.activityInfo.name + " newConfig=" + thisConfig);
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Setting activity "
+ + ar.activityInfo.name + " newConfig=" + thisConfig);
+ }
ar.newConfig = thisConfig;
}
}
}
}
if (mServices.size() > 0) {
- Iterator<Service> it = mServices.values().iterator();
- while (it.hasNext()) {
- callbacks.add(it.next());
+ for (Service service : mServices.values()) {
+ callbacks.add(service);
}
}
synchronized (mProviderMap) {
if (mLocalProviders.size() > 0) {
- Iterator<ProviderClientRecord> it = mLocalProviders.values().iterator();
- while (it.hasNext()) {
- callbacks.add(it.next().mLocalProvider);
+ for (ProviderClientRecord providerClientRecord : mLocalProviders.values()) {
+ callbacks.add(providerClientRecord.mLocalProvider);
}
}
}
@@ -3484,8 +3471,7 @@ public final class ActivityThread {
return callbacks;
}
- private final void performConfigurationChanged(
- ComponentCallbacks2 cb, Configuration config) {
+ private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
// Only for Activity objects, check that they actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
// we check the runtime type and act accordingly.
@@ -3692,7 +3678,7 @@ public final class ActivityThread {
}
}
- final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+ static final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
if (managed) {
try {
Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
@@ -3769,7 +3755,7 @@ public final class ActivityThread {
}
final int N = callbacks.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
callbacks.get(i).onTrimMemory(level);
}
WindowManagerImpl.getDefault().terminateEgl();
@@ -4057,9 +4043,7 @@ public final class ActivityThread {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
- Iterator<ProviderInfo> i = providers.iterator();
- while (i.hasNext()) {
- ProviderInfo cpi = i.next();
+ for (ProviderInfo cpi : providers) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 758ce09..0510de1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -49,6 +49,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -67,7 +68,7 @@ final class ApplicationPackageManager extends PackageManager {
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
try {
- PackageInfo pi = mPM.getPackageInfo(packageName, flags);
+ PackageInfo pi = mPM.getPackageInfo(packageName, flags, UserId.myUserId());
if (pi != null) {
return pi;
}
@@ -197,7 +198,7 @@ final class ApplicationPackageManager extends PackageManager {
public ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags);
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, UserId.myUserId());
if (ai != null) {
return ai;
}
@@ -212,7 +213,7 @@ final class ApplicationPackageManager extends PackageManager {
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ActivityInfo ai = mPM.getActivityInfo(className, flags);
+ ActivityInfo ai = mPM.getActivityInfo(className, flags, UserId.myUserId());
if (ai != null) {
return ai;
}
@@ -227,7 +228,7 @@ final class ApplicationPackageManager extends PackageManager {
public ActivityInfo getReceiverInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ActivityInfo ai = mPM.getReceiverInfo(className, flags);
+ ActivityInfo ai = mPM.getReceiverInfo(className, flags, UserId.myUserId());
if (ai != null) {
return ai;
}
@@ -242,7 +243,7 @@ final class ApplicationPackageManager extends PackageManager {
public ServiceInfo getServiceInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ServiceInfo si = mPM.getServiceInfo(className, flags);
+ ServiceInfo si = mPM.getServiceInfo(className, flags, UserId.myUserId());
if (si != null) {
return si;
}
@@ -257,7 +258,7 @@ final class ApplicationPackageManager extends PackageManager {
public ProviderInfo getProviderInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ProviderInfo pi = mPM.getProviderInfo(className, flags);
+ ProviderInfo pi = mPM.getProviderInfo(className, flags, UserId.myUserId());
if (pi != null) {
return pi;
}
@@ -422,6 +423,7 @@ final class ApplicationPackageManager extends PackageManager {
@SuppressWarnings("unchecked")
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
+ int userId = UserId.getUserId(Process.myUid());
try {
final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>();
ApplicationInfo lastItem = null;
@@ -429,7 +431,7 @@ final class ApplicationPackageManager extends PackageManager {
do {
final String lastKey = lastItem != null ? lastItem.packageName : null;
- slice = mPM.getInstalledApplications(flags, lastKey);
+ slice = mPM.getInstalledApplications(flags, lastKey, userId);
lastItem = slice.populateList(applicationInfos, ApplicationInfo.CREATOR);
} while (!slice.isLastSlice());
@@ -445,7 +447,7 @@ final class ApplicationPackageManager extends PackageManager {
return mPM.resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags);
+ flags, UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -458,7 +460,8 @@ final class ApplicationPackageManager extends PackageManager {
return mPM.queryIntentActivities(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags);
+ flags,
+ UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -490,7 +493,7 @@ final class ApplicationPackageManager extends PackageManager {
try {
return mPM.queryIntentActivityOptions(caller, specifics,
specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
- flags);
+ flags, UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -502,7 +505,8 @@ final class ApplicationPackageManager extends PackageManager {
return mPM.queryIntentReceivers(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags);
+ flags,
+ UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -514,7 +518,8 @@ final class ApplicationPackageManager extends PackageManager {
return mPM.resolveService(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags);
+ flags,
+ UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -526,7 +531,8 @@ final class ApplicationPackageManager extends PackageManager {
return mPM.queryIntentServices(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags);
+ flags,
+ UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -536,7 +542,7 @@ final class ApplicationPackageManager extends PackageManager {
public ProviderInfo resolveContentProvider(String name,
int flags) {
try {
- return mPM.resolveContentProvider(name, flags);
+ return mPM.resolveContentProvider(name, flags, UserId.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -1026,7 +1032,7 @@ final class ApplicationPackageManager extends PackageManager {
public void clearApplicationUserData(String packageName,
IPackageDataObserver observer) {
try {
- mPM.clearApplicationUserData(packageName, observer);
+ mPM.clearApplicationUserData(packageName, observer, UserId.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1139,7 +1145,7 @@ final class ApplicationPackageManager extends PackageManager {
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
try {
- mPM.setComponentEnabledSetting(componentName, newState, flags);
+ mPM.setComponentEnabledSetting(componentName, newState, flags, UserId.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1148,7 +1154,7 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
try {
- return mPM.getComponentEnabledSetting(componentName);
+ return mPM.getComponentEnabledSetting(componentName, UserId.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1159,7 +1165,7 @@ final class ApplicationPackageManager extends PackageManager {
public void setApplicationEnabledSetting(String packageName,
int newState, int flags) {
try {
- mPM.setApplicationEnabledSetting(packageName, newState, flags);
+ mPM.setApplicationEnabledSetting(packageName, newState, flags, UserId.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1168,7 +1174,7 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
- return mPM.getApplicationEnabledSetting(packageName);
+ return mPM.getApplicationEnabledSetting(packageName, UserId.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1209,6 +1215,18 @@ final class ApplicationPackageManager extends PackageManager {
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ try {
+ return mPM.getUser(userId);
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
try {
return mPM.removeUser(id);
@@ -1222,7 +1240,10 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public void updateUserName(int id, String name) {
- // TODO:
+ try {
+ mPM.updateUserName(id, name);
+ } catch (RemoteException re) {
+ }
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7043a73..d758eca 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -766,17 +766,18 @@ class ContextImpl extends Context {
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
- File f = validateFilePath(name, true);
- SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
- setFilePermissionsFromMode(f.getPath(), mode, 0);
- return db;
+ return openOrCreateDatabase(name, mode, factory, null);
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
DatabaseErrorHandler errorHandler) {
File f = validateFilePath(name, true);
- SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(), factory, errorHandler);
+ int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
+ if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+ flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
+ }
+ SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
setFilePermissionsFromMode(f.getPath(), mode, 0);
return db;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31066b5..1d994d8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -105,7 +105,7 @@ public interface IActivityManager extends IInterface {
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
- public void moveTaskToFront(int task, int flags) throws RemoteException;
+ public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
@@ -216,9 +216,10 @@ public interface IActivityManager extends IInterface {
public void enterSafeMode() throws RemoteException;
public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
-
+
public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException;
-
+ public boolean killProcessesBelowForeground(String reason) throws RemoteException;
+
// Special low-level communication with activity manager.
public void startRunning(String pkg, String cls, String action,
String data) throws RemoteException;
@@ -573,4 +574,5 @@ public interface IActivityManager extends IInterface {
int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140;
int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141;
int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142;
+ int KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+143;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 16299de..e4f7950 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1381,6 +1381,7 @@ public class Instrumentation {
}
try {
intent.setAllowFds(false);
+ intent.migrateExtraStreamToClipData();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1479,6 +1480,7 @@ public class Instrumentation {
}
try {
intent.setAllowFds(false);
+ intent.migrateExtraStreamToClipData();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index de9470e..5340fbb 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -194,7 +194,7 @@ public final class LoadedApk {
ApplicationInfo ai = null;
try {
ai = ActivityThread.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES);
+ PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
} catch (RemoteException e) {
throw new AssertionError(e);
}
@@ -351,7 +351,7 @@ public final class LoadedApk {
IPackageManager pm = ActivityThread.getPackageManager();
android.content.pm.PackageInfo pi;
try {
- pi = pm.getPackageInfo(mPackageName, 0);
+ pi = pm.getPackageInfo(mPackageName, 0, UserId.myUserId());
} catch (RemoteException e) {
throw new AssertionError(e);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5325af0..bbb6a4e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,9 +23,11 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.IntProperty;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -185,6 +187,13 @@ public class Notification implements Parcelable
*/
public RemoteViews contentView;
+
+ /**
+ * The view that will represent this notification in the pop-up "intruder alert" dialog.
+ * @hide
+ */
+ public RemoteViews intruderView;
+
/**
* The bitmap that may escape the bounds of the panel and bar.
*/
@@ -418,6 +427,64 @@ public class Notification implements Parcelable
private Bundle extras;
/**
+ * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
+ * @hide
+ */
+ private static class Action implements Parcelable {
+ public int icon;
+ public CharSequence title;
+ public PendingIntent actionIntent;
+ @SuppressWarnings("unused")
+ public Action() { }
+ private Action(Parcel in) {
+ icon = in.readInt();
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ if (in.readInt() == 1) {
+ actionIntent = PendingIntent.CREATOR.createFromParcel(in);
+ }
+ }
+ public Action(int icon_, CharSequence title_, PendingIntent intent_) {
+ this.icon = icon_;
+ this.title = title_;
+ this.actionIntent = intent_;
+ }
+ @Override
+ public Action clone() {
+ return new Action(
+ this.icon,
+ this.title.toString(),
+ this.actionIntent // safe to alias
+ );
+ }
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(icon);
+ TextUtils.writeToParcel(title, out, flags);
+ if (actionIntent != null) {
+ out.writeInt(1);
+ actionIntent.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ public static final Parcelable.Creator<Action> CREATOR
+ = new Parcelable.Creator<Action>() {
+ public Action createFromParcel(Parcel in) {
+ return new Action(in);
+ }
+ public Action[] newArray(int size) {
+ return new Action[size];
+ }
+ };
+ }
+
+ private Action[] actions;
+
+ /**
* Constructs a Notification object with default values.
* You might want to consider using {@link Builder} instead.
*/
@@ -506,12 +573,17 @@ public class Notification implements Parcelable
}
priority = parcel.readInt();
-
+
kind = parcel.createStringArray(); // may set kind to null
if (parcel.readInt() != 0) {
extras = parcel.readBundle();
}
+
+ actions = parcel.createTypedArray(Action.CREATOR);
+ if (parcel.readInt() != 0) {
+ intruderView = RemoteViews.CREATOR.createFromParcel(parcel);
+ }
}
@Override
@@ -571,6 +643,14 @@ public class Notification implements Parcelable
}
+ that.actions = new Action[this.actions.length];
+ for(int i=0; i<this.actions.length; i++) {
+ that.actions[i] = this.actions[i].clone();
+ }
+ if (this.intruderView != null) {
+ that.intruderView = this.intruderView.clone();
+ }
+
return that;
}
@@ -658,6 +738,15 @@ public class Notification implements Parcelable
} else {
parcel.writeInt(0);
}
+
+ parcel.writeTypedArray(actions, 0);
+
+ if (intruderView != null) {
+ parcel.writeInt(1);
+ intruderView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
}
/**
@@ -769,7 +858,14 @@ public class Notification implements Parcelable
sb.append(this.kind[i]);
}
}
- sb.append("])");
+ sb.append("]");
+ if (actions != null) {
+ sb.append(" ");
+ sb.append(actions.length);
+ sb.append(" action");
+ if (actions.length > 1) sb.append("s");
+ }
+ sb.append(")");
return sb.toString();
}
@@ -821,6 +917,9 @@ public class Notification implements Parcelable
private ArrayList<String> mKindList = new ArrayList<String>(1);
private Bundle mExtras;
private int mPriority;
+ private ArrayList<Action> mActions = new ArrayList<Action>(3);
+ private boolean mCanHasIntruder;
+ private boolean mIntruderActionsShowText;
/**
* Constructs a new Builder with the defaults:
@@ -1203,6 +1302,51 @@ public class Notification implements Parcelable
return this;
}
+ /**
+ * Add an action to this notification. Actions are typically displayed by
+ * the system as a button adjacent to the notification content.
+ *
+ * @param icon Resource ID of a drawable that represents the action.
+ * @param title Text describing the action.
+ * @param intent PendingIntent to be fired when the action is invoked.
+ */
+ public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
+ mActions.add(new Action(icon, title, intent));
+ return this;
+ }
+
+ /**
+ * Specify whether this notification should pop up as an
+ * "intruder alert" (a small window that shares the screen with the
+ * current activity). This sort of notification is (as the name implies)
+ * very intrusive, so use it sparingly for notifications that require
+ * the user's attention.
+ *
+ * Notes:
+ * <ul>
+ * <li>Intruder alerts only show when the screen is on.</li>
+ * <li>Intruder alerts take precedence over fullScreenIntents.</li>
+ * </ul>
+ *
+ * @param intrude Whether to pop up an intruder alert (default false).
+ */
+ public Builder setUsesIntruderAlert(boolean intrude) {
+ mCanHasIntruder = intrude;
+ return this;
+ }
+
+ /**
+ * Control text on intruder alert action buttons. By default, action
+ * buttons in intruders do not show textual labels.
+ *
+ * @param showActionText Whether to show text labels beneath action
+ * icons (default false).
+ */
+ public Builder setIntruderActionsShowText(boolean showActionText) {
+ mIntruderActionsShowText = showActionText;
+ return this;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -1284,6 +1428,45 @@ public class Notification implements Parcelable
}
}
+ private RemoteViews makeIntruderView(boolean showLabels) {
+ RemoteViews intruderView = new RemoteViews(mContext.getPackageName(),
+ R.layout.notification_intruder_content);
+ if (mLargeIcon != null) {
+ intruderView.setImageViewBitmap(R.id.icon, mLargeIcon);
+ intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
+ } else if (mSmallIcon != 0) {
+ intruderView.setImageViewResource(R.id.icon, mSmallIcon);
+ intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
+ } else {
+ intruderView.setViewVisibility(R.id.icon, View.GONE);
+ }
+ if (mContentTitle != null) {
+ intruderView.setTextViewText(R.id.title, mContentTitle);
+ }
+ if (mContentText != null) {
+ intruderView.setTextViewText(R.id.text, mContentText);
+ }
+ if (mActions.size() > 0) {
+ intruderView.setViewVisibility(R.id.actions, View.VISIBLE);
+ int N = mActions.size();
+ if (N>3) N=3;
+ final int[] BUTTONS = { R.id.action0, R.id.action1, R.id.action2 };
+ for (int i=0; i<N; i++) {
+ final Action action = mActions.get(i);
+ final int buttonId = BUTTONS[i];
+
+ intruderView.setViewVisibility(buttonId, View.VISIBLE);
+ intruderView.setTextViewText(buttonId, showLabels ? action.title : null);
+ intruderView.setTextViewCompoundDrawables(buttonId, 0, action.icon, 0, 0);
+ intruderView.setContentDescription(buttonId, action.title);
+ intruderView.setOnClickPendingIntent(buttonId, action.actionIntent);
+ }
+ } else {
+ intruderView.setViewVisibility(R.id.actions, View.GONE);
+ }
+ return intruderView;
+ }
+
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
@@ -1309,6 +1492,9 @@ public class Notification implements Parcelable
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
+ if (mCanHasIntruder) {
+ n.intruderView = makeIntruderView(mIntruderActionsShowText);
+ }
if (mLedOnMs != 0 && mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
@@ -1323,6 +1509,10 @@ public class Notification implements Parcelable
}
n.priority = mPriority;
n.extras = mExtras != null ? new Bundle(mExtras) : null;
+ if (mActions.size() > 0) {
+ n.actions = new Action[mActions.size()];
+ mActions.toArray(n.actions);
+ }
return n;
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 741a6e9..2902504 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -99,6 +99,16 @@ public abstract class Context {
public static final int MODE_MULTI_PROCESS = 0x0004;
/**
+ * Database open flag: when set, the database is opened with write-ahead
+ * logging enabled by default.
+ *
+ * @see #openOrCreateDatabase(String, int, CursorFactory)
+ * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
+ * @see SQLiteDatabase#enableWriteAheadLogging
+ */
+ public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
+
+ /**
* Flag for {@link #bindService}: automatically create the service as long
* as the binding exists. Note that while this will create the service,
* its {@link android.app.Service#onStartCommand}
@@ -691,6 +701,7 @@ public abstract class Context {
* @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
* default operation, {@link #MODE_WORLD_READABLE}
* and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+ * Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
* @param factory An optional factory class that is called to instantiate a
* cursor when query is called.
*
@@ -700,6 +711,7 @@ public abstract class Context {
* @see #MODE_PRIVATE
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
+ * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
* @see #deleteDatabase
*/
public abstract SQLiteDatabase openOrCreateDatabase(String name,
@@ -716,6 +728,7 @@ public abstract class Context {
* @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
* default operation, {@link #MODE_WORLD_READABLE}
* and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+ * Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
* @param factory An optional factory class that is called to instantiate a
* cursor when query is called.
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
@@ -726,6 +739,7 @@ public abstract class Context {
* @see #MODE_PRIVATE
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
+ * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
* @see #deleteDatabase
*/
public abstract SQLiteDatabase openOrCreateDatabase(String name,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6cf5b43..2a9f1af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2141,6 +2141,30 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_PRE_BOOT_COMPLETED =
"android.intent.action.PRE_BOOT_COMPLETED";
+ /**
+ * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
+ * userid of the new user.
+ * @hide
+ */
+ public static final String ACTION_USER_ADDED =
+ "android.intent.action.USER_ADDED";
+
+ /**
+ * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
+ * the userid of the user.
+ * @hide
+ */
+ public static final String ACTION_USER_REMOVED =
+ "android.intent.action.USER_REMOVED";
+
+ /**
+ * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
+ * the userid of the user to become the current one.
+ * @hide
+ */
+ public static final String ACTION_USER_SWITCHED =
+ "android.intent.action.USER_SWITCHED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -2682,6 +2706,13 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
+ /**
+ * The userid carried with broadcast intents related to addition, removal and switching of users
+ * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
+ * @hide
+ */
+ public static final String EXTRA_USERID =
+ "android.intent.extra.user_id";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -6467,4 +6498,56 @@ public class Intent implements Parcelable, Cloneable {
}
return type;
}
+
+ /**
+ * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
+ * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}.
+ *
+ * @hide
+ */
+ public void migrateExtraStreamToClipData() {
+ // Refuse to touch if extras already parcelled
+ if (mExtras != null && mExtras.isParcelled()) return;
+
+ // Bail when someone already gave us ClipData
+ if (getClipData() != null) return;
+
+ final String action = getAction();
+ if (ACTION_SEND.equals(action)) {
+ final Uri stream;
+ try {
+ stream = getParcelableExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
+ if (stream != null) {
+ final ClipData clipData = new ClipData(
+ null, new String[] { getType() }, new ClipData.Item(stream));
+
+ setClipData(clipData);
+ addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ }
+
+ } else if (ACTION_SEND_MULTIPLE.equals(action)) {
+ final ArrayList<Uri> streams;
+ try {
+ streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
+ if (streams != null && streams.size() > 0) {
+ final Uri firstStream = streams.get(0);
+ final ClipData clipData = new ClipData(
+ null, new String[] { getType() }, new ClipData.Item(firstStream));
+
+ final int size = streams.size();
+ for (int i = 1; i < size; i++) {
+ clipData.addItem(new ClipData.Item(streams.get(i)));
+ }
+
+ setClipData(clipData);
+ addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ }
}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index b7dfe92..06dfe90 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -326,6 +326,13 @@ public class SyncManager implements OnAccountsUpdateListener {
}
};
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ };
+
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
@@ -420,6 +427,10 @@ public class SyncManager implements OnAccountsUpdateListener {
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserIntentReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -905,6 +916,18 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId == -1) return;
+
+ // Clean up the storage engine database
+ mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+ onAccountsUpdated(null);
+ synchronized (mSyncQueue) {
+ mSyncQueue.removeUser(userId);
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 06da6fa..c18c86b 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -117,6 +117,19 @@ public class SyncQueue {
return true;
}
+ public void removeUser(int userId) {
+ ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.userId == userId) {
+ opsToRemove.add(op);
+ }
+ }
+
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
/**
* Remove the specified operation if it is in the queue.
* @param operation the operation to remove
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9bd1940..56fd5f8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -49,8 +49,8 @@ import android.content.IntentSender;
* {@hide}
*/
interface IPackageManager {
- PackageInfo getPackageInfo(String packageName, int flags);
- int getPackageUid(String packageName);
+ PackageInfo getPackageInfo(String packageName, int flags, int userId);
+ int getPackageUid(String packageName, int userId);
int[] getPackageGids(String packageName);
String[] currentToCanonicalPackageNames(in String[] names);
@@ -64,15 +64,15 @@ interface IPackageManager {
List<PermissionGroupInfo> getAllPermissionGroups(int flags);
- ApplicationInfo getApplicationInfo(String packageName, int flags);
+ ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
- ActivityInfo getActivityInfo(in ComponentName className, int flags);
+ ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
- ActivityInfo getReceiverInfo(in ComponentName className, int flags);
+ ActivityInfo getReceiverInfo(in ComponentName className, int flags, int userId);
- ServiceInfo getServiceInfo(in ComponentName className, int flags);
+ ServiceInfo getServiceInfo(in ComponentName className, int flags, int userId);
- ProviderInfo getProviderInfo(in ComponentName className, int flags);
+ ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
int checkPermission(String permName, String pkgName);
@@ -98,24 +98,24 @@ interface IPackageManager {
int getUidForSharedUser(String sharedUserName);
- ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags);
+ ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentActivities(in Intent intent,
- String resolvedType, int flags);
+ String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentActivityOptions(
in ComponentName caller, in Intent[] specifics,
in String[] specificTypes, in Intent intent,
- String resolvedType, int flags);
+ String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentReceivers(in Intent intent,
- String resolvedType, int flags);
+ String resolvedType, int flags, int userId);
ResolveInfo resolveService(in Intent intent,
- String resolvedType, int flags);
+ String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentServices(in Intent intent,
- String resolvedType, int flags);
+ String resolvedType, int flags, int userId);
/**
* This implements getInstalledPackages via a "last returned row"
@@ -131,7 +131,7 @@ interface IPackageManager {
* limit that kicks in when flags are included that bloat up the data
* returned.
*/
- ParceledListSlice getInstalledApplications(int flags, in String lastRead);
+ ParceledListSlice getInstalledApplications(int flags, in String lastRead, int userId);
/**
* Retrieve all applications that are marked as persistent.
@@ -141,7 +141,7 @@ interface IPackageManager {
*/
List<ApplicationInfo> getPersistentApplications(int flags);
- ProviderInfo resolveContentProvider(String name, int flags);
+ ProviderInfo resolveContentProvider(String name, int flags, int userId);
/**
* Retrieve sync information for all content providers.
@@ -212,28 +212,28 @@ interface IPackageManager {
* As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}.
*/
void setComponentEnabledSetting(in ComponentName componentName,
- in int newState, in int flags);
+ in int newState, in int flags, int userId);
/**
* As per {@link android.content.pm.PackageManager#getComponentEnabledSetting}.
*/
- int getComponentEnabledSetting(in ComponentName componentName);
+ int getComponentEnabledSetting(in ComponentName componentName, int userId);
/**
* As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}.
*/
- void setApplicationEnabledSetting(in String packageName, in int newState, int flags);
+ void setApplicationEnabledSetting(in String packageName, in int newState, int flags, int userId);
/**
* As per {@link android.content.pm.PackageManager#getApplicationEnabledSetting}.
*/
- int getApplicationEnabledSetting(in String packageName);
+ int getApplicationEnabledSetting(in String packageName, int userId);
/**
* Set whether the given package should be considered stopped, making
* it not visible to implicit intents that filter out stopped packages.
*/
- void setPackageStoppedState(String packageName, boolean stopped);
+ void setPackageStoppedState(String packageName, boolean stopped, int userId);
/**
* Free storage by deleting LRU sorted list of cache files across
@@ -296,7 +296,7 @@ interface IPackageManager {
* files need to be deleted
* @param observer a callback used to notify when the operation is completed.
*/
- void clearApplicationUserData(in String packageName, IPackageDataObserver observer);
+ void clearApplicationUserData(in String packageName, IPackageDataObserver observer, int userId);
/**
* Get package statistics including the code, data and cache size for
@@ -358,6 +358,7 @@ interface IPackageManager {
UserInfo createUser(in String name, int flags);
boolean removeUser(int userId);
+ void updateUserName(int userId, String name);
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
@@ -370,6 +371,7 @@ interface IPackageManager {
boolean isFirstBoot();
List<UserInfo> getUsers();
+ UserInfo getUser(int userId);
void setPermissionEnforcement(String permission, int enforcement);
int getPermissionEnforcement(String permission);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 55426b8..b06b4a5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2153,7 +2153,8 @@ public abstract class PackageManager {
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
+ COMPONENT_ENABLED_STATE_DEFAULT);
}
/**
@@ -2637,10 +2638,17 @@ public abstract class PackageManager {
public abstract void updateUserFlags(int id, int flags);
/**
- * Returns the device identity that verifiers can use to associate their
- * scheme to a particular device. This should not be used by anything other
- * than a package verifier.
- *
+ * Returns the details for the user specified by userId.
+ * @param userId the user id of the user
+ * @return UserInfo for the specified user, or null if no such user exists.
+ * @hide
+ */
+ public abstract UserInfo getUser(int userId);
+
+ /**
+ * Returns the device identity that verifiers can use to associate their scheme to a particular
+ * device. This should not be used by anything other than a package verifier.
+ *
* @return identity that uniquely identifies current device
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 207f077..eb8536f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -230,6 +230,15 @@ public class PackageParser {
return name.endsWith(".apk");
}
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
+ int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+ HashSet<String> grantedPermissions) {
+
+ return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserId.getCallingUserId());
+ }
+
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -238,9 +247,15 @@ public class PackageParser {
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
- final int userId = Binder.getOrigCallingUser();
+ return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
+ }
+
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
+ int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
@@ -248,7 +263,7 @@ public class PackageParser {
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags);
+ pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -284,7 +299,7 @@ public class PackageParser {
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- userId);
+ stopped, enabledState, userId);
}
}
}
@@ -305,7 +320,8 @@ public class PackageParser {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
+ stopped, enabledState, userId);
}
}
}
@@ -326,7 +342,8 @@ public class PackageParser {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -347,7 +364,8 @@ public class PackageParser {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -3062,11 +3080,11 @@ public class PackageParser {
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
- // User set enabled state.
- public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- // Whether the package has been stopped.
- public boolean mSetStopped = false;
+ // // User set enabled state.
+ // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ //
+ // // Whether the package has been stopped.
+ // public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
@@ -3331,9 +3349,9 @@ public class PackageParser {
}
}
- private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
- if (p.mSetEnabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- boolean enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
+ if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
@@ -3349,23 +3367,32 @@ public class PackageParser {
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
- return generateApplicationInfo(p, flags, UserId.getUserId(Binder.getCallingUid()));
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
+ int enabledState) {
+ return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ boolean stopped, int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null) && userId == 0) {
+ if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ p.applicationInfo.enabled = true;
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ p.applicationInfo.enabled = false;
+ }
+ p.applicationInfo.enabledSetting = enabledState;
return p.applicationInfo;
}
@@ -3384,18 +3411,18 @@ public class PackageParser {
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = p.mSetEnabled;
+ ai.enabledSetting = enabledState;
return ai;
}
@@ -3442,15 +3469,16 @@ public class PackageParser {
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
+ int enabledState, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
+ if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, stopped, enabledState, userId);
return ai;
}
@@ -3475,16 +3503,17 @@ public class PackageParser {
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
+ int enabledState, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)
+ if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
&& userId == UserId.getUserId(s.info.applicationInfo.uid)) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, stopped, enabledState, userId);
return si;
}
@@ -3517,9 +3546,10 @@ public class PackageParser {
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
+ int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, p.metaData)
+ if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
|| p.info.uriPermissionPatterns == null)
&& userId == 0) {
@@ -3531,7 +3561,7 @@ public class PackageParser {
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
return pi;
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e2c222b..254f652 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -211,8 +211,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
setPageSize();
- setSyncModeFromConfiguration();
- setJournalModeFromConfiguration();
+ setForeignKeyModeFromConfiguration();
+ setWalModeFromConfiguration();
setJournalSizeLimit();
setAutoCheckpointInterval();
setLocaleFromConfiguration();
@@ -268,28 +268,79 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
}
- private void setSyncModeFromConfiguration() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final String newValue = mConfiguration.syncMode;
- String value = executeForString("PRAGMA synchronous", null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- execute("PRAGMA synchronous=" + newValue, null, null);
+ private void setForeignKeyModeFromConfiguration() {
+ if (!mIsReadOnlyConnection) {
+ final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0;
+ long value = executeForLong("PRAGMA foreign_keys", null, null);
+ if (value != newValue) {
+ execute("PRAGMA foreign_keys=" + newValue, null, null);
}
}
}
- private void setJournalModeFromConfiguration() {
+ private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final String newValue = mConfiguration.journalMode;
- String value = executeForString("PRAGMA journal_mode", null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- value = executeForString("PRAGMA journal_mode=" + newValue, null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- Log.e(TAG, "setting journal_mode to " + newValue
- + " failed for db: " + mConfiguration.label
- + " (on pragma set journal_mode, sqlite returned:" + value);
+ if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+ setJournalMode("WAL");
+ setSyncMode(SQLiteGlobal.getWALSyncMode());
+ } else {
+ setJournalMode(SQLiteGlobal.getDefaultJournalMode());
+ setSyncMode(SQLiteGlobal.getDefaultSyncMode());
+ }
+ }
+ }
+
+ private void setSyncMode(String newValue) {
+ String value = executeForString("PRAGMA synchronous", null, null);
+ if (!canonicalizeSyncMode(value).equalsIgnoreCase(
+ canonicalizeSyncMode(newValue))) {
+ execute("PRAGMA synchronous=" + newValue, null, null);
+ }
+ }
+
+ private static String canonicalizeSyncMode(String value) {
+ if (value.equals("0")) {
+ return "OFF";
+ } else if (value.equals("1")) {
+ return "NORMAL";
+ } else if (value.equals("2")) {
+ return "FULL";
+ }
+ return value;
+ }
+
+ private void setJournalMode(String newValue) {
+ String value = executeForString("PRAGMA journal_mode", null, null);
+ if (!value.equalsIgnoreCase(newValue)) {
+ try {
+ String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
+ if (result.equalsIgnoreCase(newValue)) {
+ return;
}
+ // PRAGMA journal_mode silently fails and returns the original journal
+ // mode in some cases if the journal mode could not be changed.
+ } catch (SQLiteDatabaseLockedException ex) {
+ // This error (SQLITE_BUSY) occurs if one connection has the database
+ // open in WAL mode and another tries to change it to non-WAL.
}
+ // Because we always disable WAL mode when a database is first opened
+ // (even if we intend to re-enable it), we can encounter problems if
+ // there is another open connection to the database somewhere.
+ // This can happen for a variety of reasons such as an application opening
+ // the same database in multiple processes at the same time or if there is a
+ // crashing content provider service that the ActivityManager has
+ // removed from its registry but whose process hasn't quite died yet
+ // by the time it is restarted in a new process.
+ //
+ // If we don't change the journal mode, nothing really bad happens.
+ // In the worst case, an application that enables WAL might not actually
+ // get it, although it can still use connection pooling.
+ Log.w(TAG, "Could not change the database journal mode of '"
+ + mConfiguration.label + "' from '" + value + "' to '" + newValue
+ + "' because the database is locked. This usually means that "
+ + "there are other open connections to the database which prevents "
+ + "the database from enabling or disabling write-ahead logging mode. "
+ + "Proceeding without changing the journal mode.");
}
}
@@ -349,10 +400,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
// Remember what changed.
- boolean syncModeChanged = !configuration.syncMode.equalsIgnoreCase(
- mConfiguration.syncMode);
- boolean journalModeChanged = !configuration.journalMode.equalsIgnoreCase(
- mConfiguration.journalMode);
+ boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
+ != mConfiguration.foreignKeyConstraintsEnabled;
+ boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
+ & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
// Update configuration parameters.
@@ -361,14 +412,14 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
// Update prepared statement cache size.
mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
- // Update sync mode.
- if (syncModeChanged) {
- setSyncModeFromConfiguration();
+ // Update foreign key mode.
+ if (foreignKeyModeChanged) {
+ setForeignKeyModeFromConfiguration();
}
- // Update journal mode.
- if (journalModeChanged) {
- setJournalModeFromConfiguration();
+ // Update WAL.
+ if (walModeChanged) {
+ setWalModeFromConfiguration();
}
// Update locale.
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 3562e89..5c8e38b 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -81,6 +81,7 @@ public final class SQLiteConnectionPool implements Closeable {
private final Object mLock = new Object();
private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
private final SQLiteDatabaseConfiguration mConfiguration;
+ private int mMaxConnectionPoolSize;
private boolean mIsOpen;
private int mNextConnectionId;
@@ -146,6 +147,7 @@ public final class SQLiteConnectionPool implements Closeable {
private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
mConfiguration = new SQLiteDatabaseConfiguration(configuration);
+ setMaxConnectionPoolSizeLocked();
}
@Override
@@ -257,7 +259,46 @@ public final class SQLiteConnectionPool implements Closeable {
synchronized (mLock) {
throwIfClosedLocked();
+ boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
+ & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ if (walModeChanged) {
+ // WAL mode can only be changed if there are no acquired connections
+ // because we need to close all but the primary connection first.
+ if (!mAcquiredConnections.isEmpty()) {
+ throw new IllegalStateException("Write Ahead Logging (WAL) mode cannot "
+ + "be enabled or disabled while there are transactions in "
+ + "progress. Finish all transactions and release all active "
+ + "database connections first.");
+ }
+
+ // Close all non-primary connections. This should happen immediately
+ // because none of them are in use.
+ closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
+ assert mAvailableNonPrimaryConnections.isEmpty();
+ }
+
+ boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
+ != mConfiguration.foreignKeyConstraintsEnabled;
+ if (foreignKeyModeChanged) {
+ // Foreign key constraints can only be changed if there are no transactions
+ // in progress. To make this clear, we throw an exception if there are
+ // any acquired connections.
+ if (!mAcquiredConnections.isEmpty()) {
+ throw new IllegalStateException("Foreign Key Constraints cannot "
+ + "be enabled or disabled while there are transactions in "
+ + "progress. Finish all transactions and release all active "
+ + "database connections first.");
+ }
+ }
+
if (mConfiguration.openFlags != configuration.openFlags) {
+ // If we are changing open flags and WAL mode at the same time, then
+ // we have no choice but to close the primary connection beforehand
+ // because there can only be one connection open when we change WAL mode.
+ if (walModeChanged) {
+ closeAvailableConnectionsAndLogExceptionsLocked();
+ }
+
// Try to reopen the primary connection using the new open flags then
// close and discard all existing connections.
// This might throw if the database is corrupt or cannot be opened in
@@ -270,9 +311,11 @@ public final class SQLiteConnectionPool implements Closeable {
mAvailablePrimaryConnection = newPrimaryConnection;
mConfiguration.updateParametersFrom(configuration);
+ setMaxConnectionPoolSizeLocked();
} else {
// Reconfigure the database connections in place.
mConfiguration.updateParametersFrom(configuration);
+ setMaxConnectionPoolSizeLocked();
closeExcessConnectionsAndLogExceptionsLocked();
reconfigureAllConnectionsLocked();
@@ -334,8 +377,7 @@ public final class SQLiteConnectionPool implements Closeable {
mAvailablePrimaryConnection = connection;
}
wakeConnectionWaitersLocked();
- } else if (mAvailableNonPrimaryConnections.size() >=
- mConfiguration.maxConnectionPoolSize - 1) {
+ } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
closeConnectionAndLogExceptionsLocked(connection);
} else {
if (recycleConnectionLocked(connection, status)) {
@@ -453,11 +495,7 @@ public final class SQLiteConnectionPool implements Closeable {
// Can't throw.
private void closeAvailableConnectionsAndLogExceptionsLocked() {
- final int count = mAvailableNonPrimaryConnections.size();
- for (int i = 0; i < count; i++) {
- closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
- }
- mAvailableNonPrimaryConnections.clear();
+ closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
if (mAvailablePrimaryConnection != null) {
closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
@@ -466,9 +504,18 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
+ final int count = mAvailableNonPrimaryConnections.size();
+ for (int i = 0; i < count; i++) {
+ closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
+ }
+ mAvailableNonPrimaryConnections.clear();
+ }
+
+ // Can't throw.
private void closeExcessConnectionsAndLogExceptionsLocked() {
int availableCount = mAvailableNonPrimaryConnections.size();
- while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
+ while (availableCount-- > mMaxConnectionPoolSize - 1) {
SQLiteConnection connection =
mAvailableNonPrimaryConnections.remove(availableCount);
closeConnectionAndLogExceptionsLocked(connection);
@@ -843,7 +890,7 @@ public final class SQLiteConnectionPool implements Closeable {
if (mAvailablePrimaryConnection != null) {
openConnections += 1;
}
- if (openConnections >= mConfiguration.maxConnectionPoolSize) {
+ if (openConnections >= mMaxConnectionPoolSize) {
return null;
}
connection = openConnectionLocked(mConfiguration,
@@ -895,6 +942,18 @@ public final class SQLiteConnectionPool implements Closeable {
return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
}
+ private void setMaxConnectionPoolSizeLocked() {
+ if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+ mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
+ } else {
+ // TODO: We don't actually need to restrict the connection pool size to 1
+ // for non-WAL databases. There might be reasons to use connection pooling
+ // with other journal modes. For now, enabling connection pooling and
+ // using WAL are the same thing in the API.
+ mMaxConnectionPoolSize = 1;
+ }
+ }
+
private void throwIfClosedLocked() {
if (!mIsOpen) {
throw new IllegalStateException("Cannot perform this operation "
@@ -941,7 +1000,7 @@ public final class SQLiteConnectionPool implements Closeable {
synchronized (mLock) {
printer.println("Connection pool for " + mConfiguration.path + ":");
printer.println(" Open: " + mIsOpen);
- printer.println(" Max connections: " + mConfiguration.maxConnectionPoolSize);
+ printer.println(" Max connections: " + mMaxConnectionPoolSize);
printer.println(" Available primary connection:");
if (mAvailablePrimaryConnection != null) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index bf32ea7..7bd0c8d 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -127,10 +127,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
// INVARIANT: Guarded by mLock.
private boolean mHasAttachedDbsLocked;
- // True if the database is in WAL mode.
- // INVARIANT: Guarded by mLock.
- private boolean mIsWALEnabledLocked;
-
/**
* When a constraint violation occurs, an immediate ROLLBACK occurs,
* thus ending the current transaction, and the command aborts with a
@@ -236,6 +232,18 @@ public final class SQLiteDatabase extends SQLiteClosable {
public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
/**
+ * Open flag: Flag for {@link #openDatabase} to open the database file with
+ * write-ahead logging enabled by default. Using this flag is more efficient
+ * than calling {@link #enableWriteAheadLogging}.
+ *
+ * Write-ahead logging cannot be used with read-only databases so the value of
+ * this flag is ignored if the database is opened read-only.
+ *
+ * @see #enableWriteAheadLogging
+ */
+ public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
+
+ /**
* Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
*
* Each prepared-statement is between 1K - 6K, depending on the complexity of the
@@ -658,7 +666,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @throws SQLiteException if the database cannot be opened
*/
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
- return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
+ return openDatabase(path, factory, flags, null);
}
/**
@@ -698,7 +706,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
* Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
*/
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
- return openDatabase(path, factory, CREATE_IF_NECESSARY);
+ return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
}
/**
@@ -834,8 +842,14 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
mConfigurationLocked.customFunctions.add(wrapper);
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.customFunctions.remove(wrapper);
+ throw ex;
+ }
}
}
@@ -1733,8 +1747,15 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
+ final Locale oldLocale = mConfigurationLocked.locale;
mConfigurationLocked.locale = locale;
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.locale = oldLocale;
+ throw ex;
+ }
}
}
@@ -1759,58 +1780,144 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
+ final int oldMaxSqlCacheSize = mConfigurationLocked.maxSqlCacheSize;
mConfigurationLocked.maxSqlCacheSize = cacheSize;
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.maxSqlCacheSize = oldMaxSqlCacheSize;
+ throw ex;
+ }
}
}
/**
- * This method enables parallel execution of queries from multiple threads on the same database.
- * It does this by opening multiple handles to the database and using a different
- * database handle for each query.
+ * Sets whether foreign key constraints are enabled for the database.
* <p>
- * If a transaction is in progress on one connection handle and say, a table is updated in the
- * transaction, then query on the same table on another connection handle will block for the
- * transaction to complete. But this method enables such queries to execute by having them
- * return old version of the data from the table. Most often it is the data that existed in the
- * table prior to the above transaction updates on that table.
+ * By default, foreign key constraints are not enforced by the database.
+ * This method allows an application to enable foreign key constraints.
+ * It must be called each time the database is opened to ensure that foreign
+ * key constraints are enabled for the session.
+ * </p><p>
+ * A good time to call this method is right after calling {@link #openOrCreateDatabase}
+ * or in the {@link SQLiteOpenHelper#onConfigure} callback.
+ * </p><p>
+ * When foreign key constraints are disabled, the database does not check whether
+ * changes to the database will violate foreign key constraints. Likewise, when
+ * foreign key constraints are disabled, the database will not execute cascade
+ * delete or update triggers. As a result, it is possible for the database
+ * state to become inconsistent. To perform a database integrity check,
+ * call {@link #isDatabaseIntegrityOk}.
+ * </p><p>
+ * This method must not be called while a transaction is in progress.
+ * </p><p>
+ * See also <a href="http://sqlite.org/foreignkeys.html">SQLite Foreign Key Constraints</a>
+ * for more details about foreign key constraint support.
+ * </p>
+ *
+ * @param enable True to enable foreign key constraints, false to disable them.
+ *
+ * @throws IllegalStateException if the are transactions is in progress
+ * when this method is called.
+ */
+ public void setForeignKeyConstraintsEnabled(boolean enable) {
+ synchronized (mLock) {
+ throwIfNotOpenLocked();
+
+ if (mConfigurationLocked.foreignKeyConstraintsEnabled == enable) {
+ return;
+ }
+
+ mConfigurationLocked.foreignKeyConstraintsEnabled = enable;
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.foreignKeyConstraintsEnabled = !enable;
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * This method enables parallel execution of queries from multiple threads on the
+ * same database. It does this by opening multiple connections to the database
+ * and using a different database connection for each query. The database
+ * journal mode is also changed to enable writes to proceed concurrently with reads.
* <p>
- * Maximum number of simultaneous handles used to execute queries in parallel is
+ * When write-ahead logging is not enabled (the default), it is not possible for
+ * reads and writes to occur on the database at the same time. Before modifying the
+ * database, the writer implicitly acquires an exclusive lock on the database which
+ * prevents readers from accessing the database until the write is completed.
+ * </p><p>
+ * In contrast, when write-ahead logging is enabled (by calling this method), write
+ * operations occur in a separate log file which allows reads to proceed concurrently.
+ * While a write is in progress, readers on other threads will perceive the state
+ * of the database as it was before the write began. When the write completes, readers
+ * on other threads will then perceive the new state of the database.
+ * </p><p>
+ * It is a good idea to enable write-ahead logging whenever a database will be
+ * concurrently accessed and modified by multiple threads at the same time.
+ * However, write-ahead logging uses significantly more memory than ordinary
+ * journaling because there are multiple connections to the same database.
+ * So if a database will only be used by a single thread, or if optimizing
+ * concurrency is not very important, then write-ahead logging should be disabled.
+ * </p><p>
+ * After calling this method, execution of queries in parallel is enabled as long as
+ * the database remains open. To disable execution of queries in parallel, either
+ * call {@link #disableWriteAheadLogging} or close the database and reopen it.
+ * </p><p>
+ * The maximum number of connections used to execute queries in parallel is
* dependent upon the device memory and possibly other properties.
- * <p>
- * After calling this method, execution of queries in parallel is enabled as long as this
- * database handle is open. To disable execution of queries in parallel, database should
- * be closed and reopened.
- * <p>
+ * </p><p>
* If a query is part of a transaction, then it is executed on the same database handle the
* transaction was begun.
- * <p>
- * If the database has any attached databases, then execution of queries in paralel is NOT
- * possible. In such cases, a message is printed to logcat and false is returned.
- * <p>
- * This feature is not available for :memory: databases. In such cases,
- * a message is printed to logcat and false is returned.
- * <p>
- * A typical way to use this method is the following:
- * <pre>
- * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
- * CREATE_IF_NECESSARY, myDatabaseErrorHandler);
- * db.enableWriteAheadLogging();
- * </pre>
- * <p>
+ * </p><p>
* Writers should use {@link #beginTransactionNonExclusive()} or
* {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
- * to start a trsnsaction.
- * Non-exclusive mode allows database file to be in readable by threads executing queries.
+ * to start a transaction. Non-exclusive mode allows database file to be in readable
+ * by other threads executing queries.
+ * </p><p>
+ * If the database has any attached databases, then execution of queries in parallel is NOT
+ * possible. Likewise, write-ahead logging is not supported for read-only databases
+ * or memory databases. In such cases, {@link #enableWriteAheadLogging} returns false.
+ * </p><p>
+ * The best way to enable write-ahead logging is to pass the
+ * {@link #ENABLE_WRITE_AHEAD_LOGGING} flag to {@link #openDatabase}. This is
+ * more efficient than calling {@link #enableWriteAheadLogging}.
+ * <code><pre>
+ * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+ * SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
+ * myDatabaseErrorHandler);
+ * db.enableWriteAheadLogging();
+ * </pre></code>
+ * </p><p>
+ * Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging}
+ * after opening the database.
+ * <code><pre>
+ * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+ * SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler);
+ * db.enableWriteAheadLogging();
+ * </pre></code>
+ * </p><p>
+ * See also <a href="http://sqlite.org/wal.html">SQLite Write-Ahead Logging</a> for
+ * more details about how write-ahead logging works.
* </p>
*
- * @return true if write-ahead-logging is set. false otherwise
+ * @return True if write-ahead logging is enabled.
+ *
+ * @throws IllegalStateException if there are transactions in progress at the
+ * time this method is called. WAL mode can only be changed when there are no
+ * transactions in progress.
+ *
+ * @see #ENABLE_WRITE_AHEAD_LOGGING
+ * @see #disableWriteAheadLogging
*/
public boolean enableWriteAheadLogging() {
synchronized (mLock) {
throwIfNotOpenLocked();
- if (mIsWALEnabledLocked) {
+ if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0) {
return true;
}
@@ -1835,32 +1942,57 @@ public final class SQLiteDatabase extends SQLiteClosable {
return false;
}
- mIsWALEnabledLocked = true;
- mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
- mConfigurationLocked.syncMode = SQLiteGlobal.getWALSyncMode();
- mConfigurationLocked.journalMode = "WAL";
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
+ throw ex;
+ }
}
return true;
}
/**
* This method disables the features enabled by {@link #enableWriteAheadLogging()}.
- * @hide
+ *
+ * @throws IllegalStateException if there are transactions in progress at the
+ * time this method is called. WAL mode can only be changed when there are no
+ * transactions in progress.
+ *
+ * @see #enableWriteAheadLogging
*/
public void disableWriteAheadLogging() {
synchronized (mLock) {
throwIfNotOpenLocked();
- if (!mIsWALEnabledLocked) {
+ if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
return;
}
- mIsWALEnabledLocked = false;
- mConfigurationLocked.maxConnectionPoolSize = 1;
- mConfigurationLocked.syncMode = SQLiteGlobal.getDefaultSyncMode();
- mConfigurationLocked.journalMode = SQLiteGlobal.getDefaultJournalMode();
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * Returns true if write-ahead logging has been enabled for this database.
+ *
+ * @return True if write-ahead logging has been enabled for this database.
+ *
+ * @see #enableWriteAheadLogging
+ * @see #ENABLE_WRITE_AHEAD_LOGGING
+ */
+ public boolean isWriteAheadLoggingEnabled() {
+ synchronized (mLock) {
+ throwIfNotOpenLocked();
+
+ return (mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index efbcaca..549ab90 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -62,14 +62,6 @@ public final class SQLiteDatabaseConfiguration {
public int openFlags;
/**
- * The maximum number of connections to retain in the connection pool.
- * Must be at least 1.
- *
- * Default is 1.
- */
- public int maxConnectionPoolSize;
-
- /**
* The maximum size of the prepared statement cache for each database connection.
* Must be non-negative.
*
@@ -85,18 +77,11 @@ public final class SQLiteDatabaseConfiguration {
public Locale locale;
/**
- * The database synchronization mode.
- *
- * Default is {@link SQLiteGlobal#getDefaultSyncMode()}.
- */
- public String syncMode;
-
- /**
- * The database journal mode.
+ * True if foreign key constraints are enabled.
*
- * Default is {@link SQLiteGlobal#getDefaultJournalMode()}.
+ * Default is false.
*/
- public String journalMode;
+ public boolean foreignKeyConstraintsEnabled;
/**
* The custom functions to register.
@@ -121,11 +106,8 @@ public final class SQLiteDatabaseConfiguration {
this.openFlags = openFlags;
// Set default values for optional parameters.
- maxConnectionPoolSize = 1;
maxSqlCacheSize = 25;
locale = Locale.getDefault();
- syncMode = SQLiteGlobal.getDefaultSyncMode();
- journalMode = SQLiteGlobal.getDefaultJournalMode();
}
/**
@@ -159,11 +141,9 @@ public final class SQLiteDatabaseConfiguration {
}
openFlags = other.openFlags;
- maxConnectionPoolSize = other.maxConnectionPoolSize;
maxSqlCacheSize = other.maxSqlCacheSize;
locale = other.locale;
- syncMode = other.syncMode;
- journalMode = other.journalMode;
+ foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
customFunctions.clear();
customFunctions.addAll(other.customFunctions);
}
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index ffa4663..431eca2 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -58,6 +58,7 @@ public abstract class SQLiteOpenHelper {
private SQLiteDatabase mDatabase;
private boolean mIsInitializing;
+ private boolean mEnableWriteAheadLogging;
private final DatabaseErrorHandler mErrorHandler;
/**
@@ -74,7 +75,7 @@ public abstract class SQLiteOpenHelper {
* newer, {@link #onDowngrade} will be used to downgrade the database
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
- this(context, name, factory, version, new DefaultDatabaseErrorHandler());
+ this(context, name, factory, version, null);
}
/**
@@ -92,14 +93,11 @@ public abstract class SQLiteOpenHelper {
* {@link #onUpgrade} will be used to upgrade the database; if the database is
* newer, {@link #onDowngrade} will be used to downgrade the database
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
- * corruption.
+ * corruption, or null to use the default error handler.
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
- if (errorHandler == null) {
- throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
- }
mContext = context;
mName = name;
@@ -117,6 +115,32 @@ public abstract class SQLiteOpenHelper {
}
/**
+ * Enables or disables the use of write-ahead logging for the database.
+ *
+ * Write-ahead logging cannot be used with read-only databases so the value of
+ * this flag is ignored if the database is opened read-only.
+ *
+ * @param enabled True if write-ahead logging should be enabled, false if it
+ * should be disabled.
+ *
+ * @see SQLiteDatabase#enableWriteAheadLogging()
+ */
+ public void setWriteAheadLoggingEnabled(boolean enabled) {
+ synchronized (this) {
+ if (mEnableWriteAheadLogging != enabled) {
+ if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
+ if (enabled) {
+ mDatabase.enableWriteAheadLogging();
+ } else {
+ mDatabase.disableWriteAheadLogging();
+ }
+ }
+ mEnableWriteAheadLogging = enabled;
+ }
+ }
+ }
+
+ /**
* Create and/or open a database that will be used for reading and writing.
* The first time this is called, the database will be opened and
* {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
@@ -197,7 +221,9 @@ public abstract class SQLiteOpenHelper {
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
- db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
+ db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
+ Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
+ mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
@@ -211,6 +237,8 @@ public abstract class SQLiteOpenHelper {
}
}
+ onConfigure(db);
+
final int version = db.getVersion();
if (version != mNewVersion) {
if (db.isReadOnly()) {
@@ -235,6 +263,7 @@ public abstract class SQLiteOpenHelper {
db.endTransaction();
}
}
+
onOpen(db);
if (db.isReadOnly()) {
@@ -264,6 +293,25 @@ public abstract class SQLiteOpenHelper {
}
/**
+ * Called when the database connection is being configured, to enable features
+ * such as write-ahead logging or foreign key support.
+ * <p>
+ * This method is called before {@link #onCreate}, {@link #onUpgrade},
+ * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify
+ * the database except to configure the database connection as required.
+ * </p><p>
+ * This method should only call methods that configure the parameters of the
+ * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
+ * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
+ * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize},
+ * or executing PRAGMA statements.
+ * </p>
+ *
+ * @param db The database.
+ */
+ public void onConfigure(SQLiteDatabase db) {}
+
+ /**
* Called when the database is created for the first time. This is where the
* creation of tables and the initial population of the tables should happen.
*
@@ -276,11 +324,16 @@ public abstract class SQLiteOpenHelper {
* should use this method to drop tables, add tables, or do anything else it
* needs to upgrade to the new schema version.
*
- * <p>The SQLite ALTER TABLE documentation can be found
+ * <p>
+ * The SQLite ALTER TABLE documentation can be found
* <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
* you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
* you can use ALTER TABLE to rename the old table, then create the new table and then
* populate the new table with the contents of the old table.
+ * </p><p>
+ * This method executes within a transaction. If an exception is thrown, all changes
+ * will automatically be rolled back.
+ * </p>
*
* @param db The database.
* @param oldVersion The old database version.
@@ -290,11 +343,16 @@ public abstract class SQLiteOpenHelper {
/**
* Called when the database needs to be downgraded. This is strictly similar to
- * onUpgrade() method, but is called whenever current version is newer than requested one.
+ * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
* However, this method is not abstract, so it is not mandatory for a customer to
* implement it. If not overridden, default implementation will reject downgrade and
* throws SQLiteException
*
+ * <p>
+ * This method executes within a transaction. If an exception is thrown, all changes
+ * will automatically be rolled back.
+ * </p>
+ *
* @param db The database.
* @param oldVersion The old database version.
* @param newVersion The new database version.
@@ -308,6 +366,12 @@ public abstract class SQLiteOpenHelper {
* Called when the database has been opened. The implementation
* should check {@link SQLiteDatabase#isReadOnly} before updating the
* database.
+ * <p>
+ * This method is called after the database connection has been configured
+ * and after the database schema has been created, upgraded or downgraded as necessary.
+ * If the database connection must be configured in some way before the schema
+ * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
+ * </p>
*
* @param db The database.
*/
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 43efb03..9410243 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -398,16 +398,16 @@ public final class SQLiteSession {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked(cancellationSignal);
+ endTransactionUnchecked(cancellationSignal, false);
}
- private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+ private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
- boolean successful = top.mMarkedSuccessful && !top.mChildFailed;
+ boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
@@ -534,7 +534,7 @@ public final class SQLiteSession {
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancellationSignal); // might throw
+ endTransactionUnchecked(cancellationSignal, true); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java
deleted file mode 100644
index ed6d103..0000000
--- a/core/java/android/net/Downloads.java
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.provider.BaseColumns;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.File;
-import java.io.InputStream;
-
-/**
- * The Download Manager
- *
- * @hide
- */
-public final class Downloads {
-
-
- /**
- * Download status codes
- */
-
- /**
- * This download hasn't started yet
- */
- public static final int STATUS_PENDING = 190;
-
- /**
- * This download has started
- */
- public static final int STATUS_RUNNING = 192;
-
- /**
- * This download has successfully completed.
- * Warning: there might be other status values that indicate success
- * in the future.
- * Use isSucccess() to capture the entire category.
- */
- public static final int STATUS_SUCCESS = 200;
-
- /**
- * This download can't be performed because the content type cannot be
- * handled.
- */
- public static final int STATUS_NOT_ACCEPTABLE = 406;
-
- /**
- * This download has completed with an error.
- * Warning: there will be other status values that indicate errors in
- * the future. Use isStatusError() to capture the entire category.
- */
- public static final int STATUS_UNKNOWN_ERROR = 491;
-
- /**
- * This download couldn't be completed because of an HTTP
- * redirect response that the download manager couldn't
- * handle.
- */
- public static final int STATUS_UNHANDLED_REDIRECT = 493;
-
- /**
- * This download couldn't be completed due to insufficient storage
- * space. Typically, this is because the SD card is full.
- */
- public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
-
- /**
- * This download couldn't be completed because no external storage
- * device was found. Typically, this is because the SD card is not
- * mounted.
- */
- public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
-
- /**
- * Returns whether the status is a success (i.e. 2xx).
- */
- public static boolean isStatusSuccess(int status) {
- return (status >= 200 && status < 300);
- }
-
- /**
- * Returns whether the status is an error (i.e. 4xx or 5xx).
- */
- public static boolean isStatusError(int status) {
- return (status >= 400 && status < 600);
- }
-
- /**
- * Download destinations
- */
-
- /**
- * This download will be saved to the external storage. This is the
- * default behavior, and should be used for any file that the user
- * can freely access, copy, delete. Even with that destination,
- * unencrypted DRM files are saved in secure internal storage.
- * Downloads to the external destination only write files for which
- * there is a registered handler. The resulting files are accessible
- * by filename to all applications.
- */
- public static final int DOWNLOAD_DESTINATION_EXTERNAL = 1;
-
- /**
- * This download will be saved to the download manager's private
- * partition. This is the behavior used by applications that want to
- * download private files that are used and deleted soon after they
- * get downloaded. All file types are allowed, and only the initiating
- * application can access the file (indirectly through a content
- * provider). This requires the
- * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
- */
- public static final int DOWNLOAD_DESTINATION_CACHE = 2;
-
- /**
- * This download will be saved to the download manager's private
- * partition and will be purged as necessary to make space. This is
- * for private files (similar to CACHE_PARTITION) that aren't deleted
- * immediately after they are used, and are kept around by the download
- * manager as long as space is available.
- */
- public static final int DOWNLOAD_DESTINATION_CACHE_PURGEABLE = 3;
-
-
- /**
- * An invalid download id
- */
- public static final long DOWNLOAD_ID_INVALID = -1;
-
-
- /**
- * Broadcast Action: this is sent by the download manager to the app
- * that had initiated a download when that download completes. The
- * download's content: uri is specified in the intent's data.
- */
- public static final String ACTION_DOWNLOAD_COMPLETED =
- "android.intent.action.DOWNLOAD_COMPLETED";
-
- /**
- * If extras are specified when requesting a download they will be provided in the intent that
- * is sent to the specified class and package when a download has finished.
- * <P>Type: TEXT</P>
- * <P>Owner can Init</P>
- */
- public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
-
-
- /**
- * Status class for a download
- */
- public static final class StatusInfo {
- public boolean completed = false;
- /** The filename of the active download. */
- public String filename = null;
- /** An opaque id for the download */
- public long id = DOWNLOAD_ID_INVALID;
- /** An opaque status code for the download */
- public int statusCode = -1;
- /** Approximate number of bytes downloaded so far, for debugging purposes. */
- public long bytesSoFar = -1;
-
- /**
- * Returns whether the download is completed
- * @return a boolean whether the download is complete.
- */
- public boolean isComplete() {
- return android.provider.Downloads.Impl.isStatusCompleted(statusCode);
- }
-
- /**
- * Returns whether the download is successful
- * @return a boolean whether the download is successful.
- */
- public boolean isSuccessful() {
- return android.provider.Downloads.Impl.isStatusSuccess(statusCode);
- }
- }
-
- /**
- * Class to access initiate and query download by server uri
- */
- public static final class ByUri extends DownloadBase {
- /** @hide */
- private ByUri() {}
-
- /**
- * Query where clause by app data.
- * @hide
- */
- private static final String QUERY_WHERE_APP_DATA_CLAUSE =
- android.provider.Downloads.Impl.COLUMN_APP_DATA + "=?";
-
- /**
- * Gets a Cursor pointing to the download(s) of the current system update.
- * @hide
- */
- private static final Cursor getCurrentOtaDownloads(Context context, String url) {
- return context.getContentResolver().query(
- android.provider.Downloads.Impl.CONTENT_URI,
- DOWNLOADS_PROJECTION,
- QUERY_WHERE_APP_DATA_CLAUSE,
- new String[] {url},
- null);
- }
-
- /**
- * Returns a StatusInfo with the result of trying to download the
- * given URL. Returns null if no attempts have been made.
- */
- public static final StatusInfo getStatus(
- Context context,
- String url,
- long redownload_threshold) {
- StatusInfo result = null;
- boolean hasFailedDownload = false;
- long failedDownloadModificationTime = 0;
- Cursor c = getCurrentOtaDownloads(context, url);
- try {
- while (c != null && c.moveToNext()) {
- if (result == null) {
- result = new StatusInfo();
- }
- int status = getStatusOfDownload(c, redownload_threshold);
- if (status == STATUS_DOWNLOADING_UPDATE ||
- status == STATUS_DOWNLOADED_UPDATE) {
- result.completed = (status == STATUS_DOWNLOADED_UPDATE);
- result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME);
- result.id = c.getLong(DOWNLOADS_COLUMN_ID);
- result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
- result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
- return result;
- }
-
- long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION);
- if (hasFailedDownload &&
- modTime < failedDownloadModificationTime) {
- // older than the one already in result; skip it.
- continue;
- }
-
- hasFailedDownload = true;
- failedDownloadModificationTime = modTime;
- result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
- result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return result;
- }
-
- /**
- * Query where clause for general querying.
- */
- private static final String QUERY_WHERE_CLAUSE =
- android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND " +
- android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS + "=?";
-
- /**
- * Delete all the downloads for a package/class pair.
- */
- public static final void removeAllDownloadsByPackage(
- Context context,
- String notification_package,
- String notification_class) {
- context.getContentResolver().delete(
- android.provider.Downloads.Impl.CONTENT_URI,
- QUERY_WHERE_CLAUSE,
- new String[] { notification_package, notification_class });
- }
-
- /**
- * The column for the id in the Cursor returned by
- * getProgressCursor()
- */
- public static final int getProgressColumnId() {
- return 0;
- }
-
- /**
- * The column for the current byte count in the Cursor returned by
- * getProgressCursor()
- */
- public static final int getProgressColumnCurrentBytes() {
- return 1;
- }
-
- /**
- * The column for the total byte count in the Cursor returned by
- * getProgressCursor()
- */
- public static final int getProgressColumnTotalBytes() {
- return 2;
- }
-
- /** @hide */
- private static final String[] PROJECTION = {
- BaseColumns._ID,
- android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES,
- android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES
- };
-
- /**
- * Returns a Cursor representing the progress of the download identified by the ID.
- */
- public static final Cursor getProgressCursor(Context context, long id) {
- Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI,
- String.valueOf(id));
- return context.getContentResolver().query(downloadUri, PROJECTION, null, null, null);
- }
- }
-
- /**
- * Class to access downloads by opaque download id
- */
- public static final class ById extends DownloadBase {
- /** @hide */
- private ById() {}
-
- /**
- * Get the mime tupe of the download specified by the download id
- */
- public static String getMimeTypeForId(Context context, long downloadId) {
- ContentResolver cr = context.getContentResolver();
-
- String mimeType = null;
- Cursor downloadCursor = null;
-
- try {
- Uri downloadUri = getDownloadUri(downloadId);
-
- downloadCursor = cr.query(
- downloadUri, new String[]{android.provider.Downloads.Impl.COLUMN_MIME_TYPE},
- null, null, null);
- if (downloadCursor.moveToNext()) {
- mimeType = downloadCursor.getString(0);
- }
- } finally {
- if (downloadCursor != null) downloadCursor.close();
- }
- return mimeType;
- }
-
- /**
- * Delete a download by Id
- */
- public static void deleteDownload(Context context, long downloadId) {
- ContentResolver cr = context.getContentResolver();
-
- String mimeType = null;
-
- Uri downloadUri = getDownloadUri(downloadId);
-
- cr.delete(downloadUri, null, null);
- }
-
- /**
- * Open a filedescriptor to a particular download
- */
- public static ParcelFileDescriptor openDownload(
- Context context, long downloadId, String mode)
- throws FileNotFoundException
- {
- ContentResolver cr = context.getContentResolver();
-
- String mimeType = null;
-
- Uri downloadUri = getDownloadUri(downloadId);
-
- return cr.openFileDescriptor(downloadUri, mode);
- }
-
- /**
- * Open a stream to a particular download
- */
- public static InputStream openDownloadStream(Context context, long downloadId)
- throws FileNotFoundException, IOException
- {
- ContentResolver cr = context.getContentResolver();
-
- String mimeType = null;
-
- Uri downloadUri = getDownloadUri(downloadId);
-
- return cr.openInputStream(downloadUri);
- }
-
- private static Uri getDownloadUri(long downloadId) {
- return Uri.parse(android.provider.Downloads.Impl.CONTENT_URI + "/" + downloadId);
- }
-
- /**
- * Returns a StatusInfo with the result of trying to download the
- * given URL. Returns null if no attempts have been made.
- */
- public static final StatusInfo getStatus(
- Context context,
- long downloadId) {
- StatusInfo result = null;
- boolean hasFailedDownload = false;
- long failedDownloadModificationTime = 0;
-
- Uri downloadUri = getDownloadUri(downloadId);
-
- ContentResolver cr = context.getContentResolver();
-
- Cursor c = cr.query(downloadUri, DOWNLOADS_PROJECTION, null /* selection */,
- null /* selection args */, null /* sort order */);
- try {
- if (c == null || !c.moveToNext()) {
- return result;
- }
-
- if (result == null) {
- result = new StatusInfo();
- }
- int status = getStatusOfDownload(c,0);
- if (status == STATUS_DOWNLOADING_UPDATE ||
- status == STATUS_DOWNLOADED_UPDATE) {
- result.completed = (status == STATUS_DOWNLOADED_UPDATE);
- result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME);
- result.id = c.getLong(DOWNLOADS_COLUMN_ID);
- result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
- result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
- return result;
- }
-
- long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION);
-
- result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
- result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return result;
- }
- }
-
-
- /**
- * Base class with common functionality for the various download classes
- */
- public static class DownloadBase {
- /** @hide */
- DownloadBase() {}
-
- /**
- * Initiate a download where the download will be tracked by its URI.
- */
- public static long startDownloadByUri(
- Context context,
- String url,
- String cookieData,
- boolean showDownload,
- int downloadDestination,
- boolean allowRoaming,
- boolean skipIntegrityCheck,
- String title,
- String notification_package,
- String notification_class,
- String notification_extras) {
- ContentResolver cr = context.getContentResolver();
-
- // Tell download manager to start downloading update.
- ContentValues values = new ContentValues();
- values.put(android.provider.Downloads.Impl.COLUMN_URI, url);
- values.put(android.provider.Downloads.Impl.COLUMN_COOKIE_DATA, cookieData);
- values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY,
- showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE
- : android.provider.Downloads.Impl.VISIBILITY_HIDDEN);
- if (title != null) {
- values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title);
- }
- values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url);
-
-
- // NOTE: destination should be seperated from whether the download
- // can happen when roaming
- int destination = android.provider.Downloads.Impl.DESTINATION_EXTERNAL;
- switch (downloadDestination) {
- case DOWNLOAD_DESTINATION_EXTERNAL:
- destination = android.provider.Downloads.Impl.DESTINATION_EXTERNAL;
- break;
- case DOWNLOAD_DESTINATION_CACHE:
- if (allowRoaming) {
- destination = android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION;
- } else {
- destination =
- android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING;
- }
- break;
- case DOWNLOAD_DESTINATION_CACHE_PURGEABLE:
- destination =
- android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE;
- break;
- }
- values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION, destination);
- values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY,
- skipIntegrityCheck); // Don't check ETag
- if (notification_package != null && notification_class != null) {
- values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
- notification_package);
- values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
- notification_class);
-
- if (notification_extras != null) {
- values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS,
- notification_extras);
- }
- }
-
- Uri downloadUri = cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values);
-
- long downloadId = DOWNLOAD_ID_INVALID;
- if (downloadUri != null) {
- downloadId = Long.parseLong(downloadUri.getLastPathSegment());
- }
- return downloadId;
- }
- }
-
- /** @hide */
- private static final int STATUS_INVALID = 0;
- /** @hide */
- private static final int STATUS_DOWNLOADING_UPDATE = 3;
- /** @hide */
- private static final int STATUS_DOWNLOADED_UPDATE = 4;
-
- /**
- * Column projection for the query to the download manager. This must match
- * with the constants DOWNLOADS_COLUMN_*.
- * @hide
- */
- private static final String[] DOWNLOADS_PROJECTION = {
- BaseColumns._ID,
- android.provider.Downloads.Impl.COLUMN_APP_DATA,
- android.provider.Downloads.Impl.COLUMN_STATUS,
- android.provider.Downloads.Impl._DATA,
- android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION,
- android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES,
- };
-
- /**
- * The column index for the ID.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_ID = 0;
- /**
- * The column index for the URI.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_URI = 1;
- /**
- * The column index for the status code.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_STATUS = 2;
- /**
- * The column index for the filename.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_FILENAME = 3;
- /**
- * The column index for the last modification time.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4;
- /**
- * The column index for the number of bytes downloaded so far.
- * @hide
- */
- private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5;
-
- /**
- * Gets the status of a download.
- *
- * @param c A Cursor pointing to a download. The URL column is assumed to be valid.
- * @return The status of the download.
- * @hide
- */
- private static final int getStatusOfDownload( Cursor c, long redownload_threshold) {
- int status = c.getInt(DOWNLOADS_COLUMN_STATUS);
- long realtime = SystemClock.elapsedRealtime();
-
- // TODO(dougz): special handling of 503, 404? (eg, special
- // explanatory messages to user)
-
- if (!android.provider.Downloads.Impl.isStatusCompleted(status)) {
- // Check if it's stuck
- long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION);
- long now = System.currentTimeMillis();
- if (now < modified || now - modified > redownload_threshold) {
- return STATUS_INVALID;
- }
-
- return STATUS_DOWNLOADING_UPDATE;
- }
-
- if (android.provider.Downloads.Impl.isStatusError(status)) {
- return STATUS_INVALID;
- }
-
- String filename = c.getString(DOWNLOADS_COLUMN_FILENAME);
- if (filename == null) {
- return STATUS_INVALID;
- }
-
- return STATUS_DOWNLOADED_UPDATE;
- }
-
-
- /**
- * @hide
- */
- private Downloads() {}
-}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 442535a..89c9c36 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -30,8 +30,8 @@ import android.net.NetworkTemplate;
interface INetworkPolicyManager {
/** Control UID policies. */
- void setUidPolicy(int uid, int policy);
- int getUidPolicy(int uid);
+ void setAppPolicy(int appId, int policy);
+ int getAppPolicy(int appId);
boolean isUidForeground(int uid);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 5b94784..c1f58a3 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -42,18 +42,20 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
public long lastWarningSnooze;
public long lastLimitSnooze;
public boolean metered;
+ public boolean inferred;
private static final long DEFAULT_MTU = 1500;
+ @Deprecated
public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
long warningBytes, long limitBytes, boolean metered) {
this(template, cycleDay, cycleTimezone, warningBytes, limitBytes, SNOOZE_NEVER,
- SNOOZE_NEVER, metered);
+ SNOOZE_NEVER, metered, false);
}
public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
long warningBytes, long limitBytes, long lastWarningSnooze, long lastLimitSnooze,
- boolean metered) {
+ boolean metered, boolean inferred) {
this.template = checkNotNull(template, "missing NetworkTemplate");
this.cycleDay = cycleDay;
this.cycleTimezone = checkNotNull(cycleTimezone, "missing cycleTimezone");
@@ -62,6 +64,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
this.lastWarningSnooze = lastWarningSnooze;
this.lastLimitSnooze = lastLimitSnooze;
this.metered = metered;
+ this.inferred = inferred;
}
public NetworkPolicy(Parcel in) {
@@ -73,6 +76,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
lastWarningSnooze = in.readLong();
lastLimitSnooze = in.readLong();
metered = in.readInt() != 0;
+ inferred = in.readInt() != 0;
}
@Override
@@ -85,6 +89,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
dest.writeLong(lastWarningSnooze);
dest.writeLong(lastLimitSnooze);
dest.writeInt(metered ? 1 : 0);
+ dest.writeInt(inferred ? 1 : 0);
}
@Override
@@ -134,7 +139,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
@Override
public int hashCode() {
return Objects.hashCode(template, cycleDay, cycleTimezone, warningBytes, limitBytes,
- lastWarningSnooze, lastLimitSnooze, metered);
+ lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
@Override
@@ -145,6 +150,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
&& limitBytes == other.limitBytes
&& lastWarningSnooze == other.lastWarningSnooze
&& lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+ && inferred == other.inferred
&& Objects.equal(cycleTimezone, other.cycleTimezone)
&& Objects.equal(template, other.template);
}
@@ -156,7 +162,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", cycleTimezone="
+ cycleTimezone + ", warningBytes=" + warningBytes + ", limitBytes=" + limitBytes
+ ", lastWarningSnooze=" + lastWarningSnooze + ", lastLimitSnooze="
- + lastLimitSnooze + ", metered=" + metered;
+ + lastLimitSnooze + ", metered=" + metered + ", inferred=" + inferred;
}
public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7173751..c09c676 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -88,21 +88,21 @@ public class NetworkPolicyManager {
}
/**
- * Set policy flags for specific UID.
+ * Set policy flags for specific application.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
* {@link #POLICY_REJECT_METERED_BACKGROUND}.
*/
- public void setUidPolicy(int uid, int policy) {
+ public void setAppPolicy(int appId, int policy) {
try {
- mService.setUidPolicy(uid, policy);
+ mService.setAppPolicy(appId, policy);
} catch (RemoteException e) {
}
}
- public int getUidPolicy(int uid) {
+ public int getAppPolicy(int appId) {
try {
- return mService.getUidPolicy(uid);
+ return mService.getAppPolicy(appId);
} catch (RemoteException e) {
return POLICY_NONE;
}
@@ -203,6 +203,7 @@ public class NetworkPolicyManager {
* Check if given UID can have a {@link #setUidPolicy(int, int)} defined,
* usually to protect critical system services.
*/
+ @Deprecated
public static boolean isUidValidForPolicy(Context context, int uid) {
// first, quick-reject non-applications
if (uid < android.os.Process.FIRST_APPLICATION_UID
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 5c4b258..6a4f1f2 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -18,13 +18,11 @@ package android.net;
import android.os.SystemProperties;
import android.util.Log;
-
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.cert.X509Certificate;
-
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@@ -36,7 +34,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-
import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
@@ -89,6 +86,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
private SSLSocketFactory mSecureFactory = null;
private TrustManager[] mTrustManagers = null;
private KeyManager[] mKeyManagers = null;
+ private byte[] mNpnProtocols = null;
private final int mHandshakeTimeoutMillis;
private final SSLClientSessionCache mSessionCache;
@@ -251,6 +249,60 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
/**
+ * Sets the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocols that this peer is interested in.
+ *
+ * <p>For servers this is the sequence of protocols to advertise as
+ * supported, in order of preference. This list is sent unencrypted to
+ * all clients that support NPN.
+ *
+ * <p>For clients this is a list of supported protocols to match against the
+ * server's list. If there is no protocol supported by both client and
+ * server then the first protocol in the client's list will be selected.
+ * The order of the client's protocols is otherwise insignificant.
+ *
+ * @param npnProtocols a possibly-empty list of protocol byte arrays. All
+ * arrays must be non-empty and of length less than 256.
+ */
+ public void setNpnProtocols(byte[][] npnProtocols) {
+ this.mNpnProtocols = toNpnProtocolsList(npnProtocols);
+ }
+
+ /**
+ * Returns an array containing the concatenation of length-prefixed byte
+ * strings.
+ */
+ static byte[] toNpnProtocolsList(byte[]... npnProtocols) {
+ int totalLength = 0;
+ for (byte[] s : npnProtocols) {
+ if (s.length == 0 || s.length > 255) {
+ throw new IllegalArgumentException("s.length == 0 || s.length > 255: " + s.length);
+ }
+ totalLength += 1 + s.length;
+ }
+ byte[] result = new byte[totalLength];
+ int pos = 0;
+ for (byte[] s : npnProtocols) {
+ result[pos++] = (byte) s.length;
+ for (byte b : s) {
+ result[pos++] = b;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocol selected by client and server, or
+ * null if no protocol was negotiated.
+ *
+ * @param socket a socket created by this factory.
+ */
+ public byte[] getNpnSelectedProtocol(Socket socket) {
+ return ((OpenSSLSocketImpl) socket).getNpnSelectedProtocol();
+ }
+
+ /**
* Sets the {@link KeyManager}s to be used for connections made by this factory.
*/
public void setKeyManagers(KeyManager[] keyManagers) {
@@ -271,6 +323,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -289,6 +342,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket() throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -305,6 +359,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
addr, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -319,6 +374,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(InetAddress addr, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -334,6 +390,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
host, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -350,6 +407,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(String host, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 28206b7..51cb91c 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -226,6 +226,13 @@ public final class Bundle implements Parcelable, Cloneable {
}
/**
+ * @hide
+ */
+ public boolean isParcelled() {
+ return mParcelledData != null;
+ }
+
+ /**
* Returns the number of mappings contained in this Bundle.
*
* @return the number of mappings as an int.
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
index 0da67d6..8bf6c6e 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserId.java
@@ -61,6 +61,15 @@ public final class UserId {
return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
}
+ public static boolean isApp(int uid) {
+ if (uid > 0) {
+ uid = UserId.getAppId(uid);
+ return uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID;
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns the user id for a given uid.
* @hide
@@ -96,4 +105,12 @@ public final class UserId {
public static final int getAppId(int uid) {
return uid % PER_USER_RANGE;
}
+
+ /**
+ * Returns the user id of the current process
+ * @return user id of the current process
+ */
+ public static final int myUserId() {
+ return getUserId(Process.myUid());
+ }
}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index ba4804d..bd6170b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -17,6 +17,7 @@
package android.provider;
import android.app.DownloadManager;
+import android.content.Context;
import android.net.NetworkPolicyManager;
import android.net.Uri;
@@ -742,4 +743,19 @@ public final class Downloads {
public static final String INSERT_KEY_PREFIX = "http_header_";
}
}
+
+ /**
+ * Query where clause for general querying.
+ */
+ private static final String QUERY_WHERE_CLAUSE = Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND "
+ + Impl.COLUMN_NOTIFICATION_CLASS + "=?";
+
+ /**
+ * Delete all the downloads for a package/class pair.
+ */
+ public static final void removeAllDownloadsByPackage(
+ Context context, String notification_package, String notification_class) {
+ context.getContentResolver().delete(Impl.CONTENT_URI, QUERY_WHERE_CLAUSE,
+ new String[] { notification_package, notification_class });
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fbb3273..d74ccb8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -793,6 +793,7 @@ public final class Settings {
MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS);
MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
+ MOVED_TO_SECURE.add(Secure.LOCK_BIOMETRIC_WEAK_FLAGS);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_ENABLED);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_VISIBLE);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
@@ -2657,6 +2658,13 @@ public final class Settings {
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
/**
+ * A flag containing settings used for biometric weak
+ * @hide
+ */
+ public static final String LOCK_BIOMETRIC_WEAK_FLAGS =
+ "lock_biometric_weak_flags";
+
+ /**
* Whether autolock is enabled (0 = false, 1 = true)
*/
public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index fecc8f9..850349b 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -46,6 +46,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -554,12 +555,15 @@ public class BluetoothService extends IBluetooth.Stub {
private synchronized void updateSdpRecords() {
ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
+ Resources R = mContext.getResources();
+
// Add the default records
- uuids.add(BluetoothUuid.HSP_AG);
- uuids.add(BluetoothUuid.ObexObjectPush);
+ if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
+ uuids.add(BluetoothUuid.HSP_AG);
+ uuids.add(BluetoothUuid.ObexObjectPush);
+ }
- if (mContext.getResources().
- getBoolean(com.android.internal.R.bool.config_voice_capable)) {
+ if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) {
uuids.add(BluetoothUuid.Handsfree_AG);
uuids.add(BluetoothUuid.PBAP_PSE);
}
@@ -567,14 +571,16 @@ public class BluetoothService extends IBluetooth.Stub {
// Add SDP records for profiles maintained by Android userspace
addReservedSdpRecords(uuids);
- // Enable profiles maintained by Bluez userspace.
- setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
- BluetoothPanProfileHandler.NAP_BRIDGE);
+ if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
+ // Enable profiles maintained by Bluez userspace.
+ setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
+ BluetoothPanProfileHandler.NAP_BRIDGE);
- // Add SDP records for profiles maintained by Bluez userspace
- uuids.add(BluetoothUuid.AudioSource);
- uuids.add(BluetoothUuid.AvrcpTarget);
- uuids.add(BluetoothUuid.NAP);
+ // Add SDP records for profiles maintained by Bluez userspace
+ uuids.add(BluetoothUuid.AudioSource);
+ uuids.add(BluetoothUuid.AvrcpTarget);
+ uuids.add(BluetoothUuid.NAP);
+ }
// Cannot cast uuids.toArray directly since ParcelUuid is parcelable
mAdapterUuids = new ParcelUuid[uuids.size()];
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index 1dabad2..e2aafa9 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -16,8 +16,6 @@
package android.view;
-import android.os.Handler;
-
/**
* A display lists records a series of graphics related operation and can replay
* them later. Display lists are usually built by recording operations on a
@@ -37,6 +35,30 @@ public abstract class DisplayList {
*/
public static final int FLAG_CLIP_CHILDREN = 0x1;
+ // NOTE: The STATUS_* values *must* match the enum in DrawGlInfo.h
+
+ /**
+ * Indicates that the display list is done drawing.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DONE = 0x0;
+
+ /**
+ * Indicates that the display list needs another drawing pass.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DRAW = 0x1;
+
+ /**
+ * Indicates that the display list needs to re-execute its GL functors.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ * @see HardwareCanvas#callDrawGLFunction(int)
+ */
+ public static final int STATUS_INVOKE = 0x2;
+
/**
* Starts recording the display list. All operations performed on the
* returned canvas are recorded and stored in this display list.
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index d9bf918..9639faf 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -79,25 +79,45 @@ public class FocusFinder {
switch (direction) {
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
+ setFocusBottomRight(root);
+ break;
case View.FOCUS_FORWARD:
- final int rootTop = root.getScrollY();
- final int rootLeft = root.getScrollX();
- mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusTopLeft(root);
+ } else {
+ setFocusBottomRight(root);
+ }
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
+ setFocusTopLeft(root);
+ break;
case View.FOCUS_BACKWARD:
- final int rootBottom = root.getScrollY() + root.getHeight();
- final int rootRight = root.getScrollX() + root.getWidth();
- mFocusedRect.set(rootRight, rootBottom,
- rootRight, rootBottom);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusBottomRight(root);
+ } else {
+ setFocusTopLeft(root);
break;
+ }
}
}
return findNextFocus(root, focused, mFocusedRect, direction);
}
+ private void setFocusTopLeft(ViewGroup root) {
+ final int rootBottom = root.getScrollY() + root.getHeight();
+ final int rootRight = root.getScrollX() + root.getWidth();
+ mFocusedRect.set(rootRight, rootBottom,
+ rootRight, rootBottom);
+ }
+
+ private void setFocusBottomRight(ViewGroup root) {
+ final int rootTop = root.getScrollY();
+ final int rootLeft = root.getScrollX();
+ mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ }
+
/**
* Find the next view to take focus in root's descendants, searching from
* a particular rectangle in root's coordinates.
@@ -135,22 +155,10 @@ public class FocusFinder {
final int count = focusables.size();
switch (direction) {
case View.FOCUS_FORWARD:
- if (focused != null) {
- int position = focusables.lastIndexOf(focused);
- if (position >= 0 && position + 1 < count) {
- return focusables.get(position + 1);
- }
- }
- return focusables.get(0);
+ return getForwardFocusable(focused, focusables, count);
case View.FOCUS_BACKWARD:
- if (focused != null) {
- int position = focusables.indexOf(focused);
- if (position > 0) {
- return focusables.get(position - 1);
- }
- }
- return focusables.get(count - 1);
+ return getBackwardFocusable(focused, focusables, count);
}
return null;
}
@@ -193,6 +201,38 @@ public class FocusFinder {
return closest;
}
+ private View getForwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getPreviousFocusable(focused, focusables, count) :
+ getNextFocusable(focused, focusables, count);
+ }
+
+ private View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.lastIndexOf(focused);
+ if (position >= 0 && position + 1 < count) {
+ return focusables.get(position + 1);
+ }
+ }
+ return focusables.get(0);
+ }
+
+ private View getBackwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getNextFocusable(focused, focusables, count) :
+ getPreviousFocusable(focused, focusables, count);
+ }
+
+ private View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.indexOf(focused);
+ if (position > 0) {
+ return focusables.get(position - 1);
+ }
+ }
+ return focusables.get(count - 1);
+ }
+
/**
* Is rect1 a better candidate than rect2 for a focus search in a particular
* direction from a source rect? This is the core routine that determines
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 1f75e70..0e96742 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -296,11 +296,11 @@ class GLES20Canvas extends HardwareCanvas {
///////////////////////////////////////////////////////////////////////////
@Override
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
return nCallDrawGLFunction(mRenderer, drawGLFunction);
}
- private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction);
+ private static native int nCallDrawGLFunction(int renderer, int drawGLFunction);
///////////////////////////////////////////////////////////////////////////
// Memory
@@ -394,13 +394,13 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetDisplayListName(int displayList, String name);
@Override
- public boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags) {
return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
width, height, dirty, flags);
}
- private static native boolean nDrawDisplayList(int renderer, int displayList,
+ private static native int nDrawDisplayList(int renderer, int displayList,
int width, int height, Rect dirty, int flags);
@Override
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 838c03c..2636ea2 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -59,11 +59,11 @@ public abstract class HardwareCanvas extends Canvas {
* if this method returns true, can be null.
* @param flags Optional flags about drawing, see {@link DisplayList} for
* the possible flags.
- *
- * @return True if the content of the display list requires another
- * drawing pass (invalidate()), false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public abstract boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public abstract int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags);
/**
@@ -90,10 +90,12 @@ public abstract class HardwareCanvas extends Canvas {
* This function may return true if an invalidation is needed after the call.
*
* @param drawGLFunction A native function pointer
- * @return true if an invalidate is needed after the call, false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
// Noop - this is done in the display list recorder subclass
- return false;
+ return DisplayList.STATUS_DONE;
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d08a61f..d40043f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -464,8 +464,8 @@ public abstract class HardwareRenderer {
static final Object[] sEglLock = new Object[0];
int mWidth = -1, mHeight = -1;
- static final ThreadLocal<Gl20Renderer.Gl20RendererEglContext> sEglContextStorage
- = new ThreadLocal<Gl20Renderer.Gl20RendererEglContext>();
+ static final ThreadLocal<ManagedEGLContext> sEglContextStorage
+ = new ThreadLocal<ManagedEGLContext>();
EGLContext mEglContext;
Thread mEglThread;
@@ -622,7 +622,7 @@ public abstract class HardwareRenderer {
}
}
- abstract GLES20Canvas createCanvas();
+ abstract HardwareCanvas createCanvas();
abstract int[] getConfig(boolean dirtyRegions);
@@ -662,16 +662,18 @@ public abstract class HardwareRenderer {
}
}
- Gl20Renderer.Gl20RendererEglContext managedContext = sEglContextStorage.get();
+ ManagedEGLContext managedContext = sEglContextStorage.get();
mEglContext = managedContext != null ? managedContext.getContext() : null;
mEglThread = Thread.currentThread();
if (mEglContext == null) {
mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
- sEglContextStorage.set(new Gl20Renderer.Gl20RendererEglContext(mEglContext));
+ sEglContextStorage.set(createManagedContext(mEglContext));
}
}
+ abstract ManagedEGLContext createManagedContext(EGLContext eglContext);
+
private EGLConfig chooseEglConfig() {
EGLConfig[] configs = new EGLConfig[1];
int[] configsCount = new int[1];
@@ -704,7 +706,7 @@ public abstract class HardwareRenderer {
return null;
}
- private void printConfig(EGLConfig config) {
+ private static void printConfig(EGLConfig config) {
int[] value = new int[1];
Log.d(LOG_TAG, "EGL configuration " + config + ":");
@@ -964,7 +966,6 @@ public abstract class HardwareRenderer {
Log.d("DLProperties", "getDisplayList():\t" +
mProfileData[mProfileCurrentFrame]);
}
-
}
if (displayList != null) {
@@ -973,7 +974,7 @@ public abstract class HardwareRenderer {
drawDisplayListStartTime = System.nanoTime();
}
- boolean invalidateNeeded = canvas.drawDisplayList(displayList,
+ int status = canvas.drawDisplayList(displayList,
view.getWidth(), view.getHeight(), mRedrawClip,
DisplayList.FLAG_CLIP_CHILDREN);
@@ -984,18 +985,18 @@ public abstract class HardwareRenderer {
if (ViewDebug.DEBUG_LATENCY) {
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
- total + "ms, invalidateNeeded=" +
- invalidateNeeded + ".");
+ total + "ms, status=" + status);
}
}
- if (invalidateNeeded) {
- if (mRedrawClip.isEmpty() || view.getParent() == null) {
- view.invalidate();
+ if (status != DisplayList.STATUS_DONE) {
+ if (mRedrawClip.isEmpty()) {
+ attachInfo.mViewRootImpl.invalidate();
} else {
- view.getParent().invalidateChild(view, mRedrawClip);
+ attachInfo.mViewRootImpl.invalidateChildInParent(
+ null, mRedrawClip);
+ mRedrawClip.setEmpty();
}
- mRedrawClip.setEmpty();
}
} else {
// Shouldn't reach here
@@ -1102,7 +1103,8 @@ public abstract class HardwareRenderer {
// Make sure we do this on the correct thread.
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(new Runnable() {
- @Override public void run() {
+ @Override
+ public void run() {
onTerminate(eglContext);
}
});
@@ -1117,6 +1119,7 @@ public abstract class HardwareRenderer {
GLES20Canvas.terminateCaches();
sEgl.eglDestroyContext(sEglDisplay, eglContext);
+ sEglContextStorage.set(null);
sEglContextStorage.remove();
sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
@@ -1130,7 +1133,6 @@ public abstract class HardwareRenderer {
sEglDisplay = null;
sEglConfig = null;
sPbuffer = null;
- sEglContextStorage.set(null);
}
}
}
@@ -1141,11 +1143,16 @@ public abstract class HardwareRenderer {
}
@Override
- GLES20Canvas createCanvas() {
+ HardwareCanvas createCanvas() {
return mGlCanvas = new GLES20Canvas(mTranslucent);
}
@Override
+ ManagedEGLContext createManagedContext(EGLContext eglContext) {
+ return new Gl20Renderer.Gl20RendererEglContext(mEglContext);
+ }
+
+ @Override
int[] getConfig(boolean dirtyRegions) {
return new int[] {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
@@ -1229,7 +1236,7 @@ public abstract class HardwareRenderer {
}
private static void destroyHardwareLayer(View view) {
- view.destroyLayer();
+ view.destroyLayer(true);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
@@ -1248,7 +1255,8 @@ public abstract class HardwareRenderer {
if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
if (needsContext) {
- Gl20RendererEglContext managedContext = sEglContextStorage.get();
+ Gl20RendererEglContext managedContext =
+ (Gl20RendererEglContext) sEglContextStorage.get();
if (managedContext == null) return;
usePbufferSurface(managedContext.getContext());
}
@@ -1281,7 +1289,8 @@ public abstract class HardwareRenderer {
static void trimMemory(int level) {
if (sEgl == null || sEglConfig == null) return;
- Gl20RendererEglContext managedContext = sEglContextStorage.get();
+ Gl20RendererEglContext managedContext =
+ (Gl20RendererEglContext) sEglContextStorage.get();
// We do not have OpenGL objects
if (managedContext == null) {
return;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c54d09e..14cd48f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -89,6 +89,8 @@ interface IWindowManager
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
int getPendingAppTransition();
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
+ void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
+ IRemoteCallback startedCallback);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 92e8f4e..77fd8d2 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1654,14 +1654,22 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
}
}
-
+
/**
- * Scales down the coordination of this event by the given scale.
+ * Applies a scale factor to all points within this event.
+ *
+ * This method is used to adjust touch events to simulate different density
+ * displays for compatibility mode. The values returned by {@link #getRawX()},
+ * {@link #getRawY()}, {@link #getXPrecision()} and {@link #getYPrecision()}
+ * are also affected by the scale factor.
*
+ * @param scale The scale factor to apply.
* @hide
*/
public final void scale(float scale) {
- nativeScale(mNativePtr, scale);
+ if (scale != 1.0f) {
+ nativeScale(mNativePtr, scale);
+ }
}
/** {@inheritDoc} */
@@ -2631,7 +2639,9 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @param deltaY Amount to add to the current Y coordinate of the event.
*/
public final void offsetLocation(float deltaX, float deltaY) {
- nativeOffsetLocation(mNativePtr, deltaX, deltaY);
+ if (deltaX != 0.0f || deltaY != 0.0f) {
+ nativeOffsetLocation(mNativePtr, deltaX, deltaY);
+ }
}
/**
@@ -2644,7 +2654,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public final void setLocation(float x, float y) {
float oldX = getX();
float oldY = getY();
- nativeOffsetLocation(mNativePtr, x - oldX, y - oldY);
+ offsetLocation(x - oldX, y - oldY);
}
/**
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index edaa262..eb80290d 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -20,6 +20,7 @@ import android.content.res.CompatibilityInfo.Translator;
import android.graphics.*;
import android.os.Parcelable;
import android.os.Parcel;
+import android.os.SystemProperties;
import android.util.Log;
/**
@@ -35,6 +36,15 @@ public class Surface implements Parcelable {
public static final int ROTATION_180 = 2;
public static final int ROTATION_270 = 3;
+ private static final boolean headless = "1".equals(
+ SystemProperties.get("ro.config.headless", "0"));
+
+ private static void checkHeadless() {
+ if(headless) {
+ throw new UnsupportedOperationException("Device is headless");
+ }
+ }
+
/**
* Create Surface from a {@link SurfaceTexture}.
*
@@ -46,6 +56,8 @@ public class Surface implements Parcelable {
* Surface.
*/
public Surface(SurfaceTexture surfaceTexture) {
+ checkHeadless();
+
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
@@ -244,6 +256,8 @@ public class Surface implements Parcelable {
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
+ checkHeadless();
+
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
@@ -255,6 +269,8 @@ public class Surface implements Parcelable {
public Surface(SurfaceSession s,
int pid, String name, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
+ checkHeadless();
+
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
@@ -269,6 +285,8 @@ public class Surface implements Parcelable {
* @hide
*/
public Surface() {
+ checkHeadless();
+
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index fc02cc1..83999a1 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -299,7 +299,7 @@ public class TextureView extends View {
}
@Override
- boolean destroyLayer() {
+ boolean destroyLayer(boolean valid) {
return false;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fdf3a814..2deeba6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -698,14 +698,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
/**
- * This view is enabled. Intrepretation varies by subclass.
+ * This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
*/
static final int ENABLED = 0x00000000;
/**
- * This view is disabled. Intrepretation varies by subclass.
+ * This view is disabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
*/
@@ -955,50 +955,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
/**
- * Horizontal direction of this view is from Left to Right.
- * Use with {@link #setLayoutDirection}.
- */
- public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
-
- /**
- * Horizontal direction of this view is from Right to Left.
- * Use with {@link #setLayoutDirection}.
- */
- public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
-
- /**
- * Horizontal direction of this view is inherited from its parent.
- * Use with {@link #setLayoutDirection}.
- */
- public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
-
- /**
- * Horizontal direction of this view is from deduced from the default language
- * script for the locale. Use with {@link #setLayoutDirection}.
- */
- public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
-
- /**
- * Mask for use with setFlags indicating bits used for horizontalDirection.
- * {@hide}
- */
- static final int LAYOUT_DIRECTION_MASK = 0xC0000000;
-
- /*
- * Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct
- * flag value.
- * {@hide}
- */
- private static final int[] LAYOUT_DIRECTION_FLAGS = {LAYOUT_DIRECTION_LTR,
- LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE};
-
- /**
- * Default horizontalDirection.
- * {@hide}
- */
- private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
-
- /**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add all focusable Views regardless if they are focusable in touch mode.
*/
@@ -1748,19 +1704,77 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
static final int DRAG_HOVERED = 0x00000002;
/**
- * Indicates whether the view layout direction has been resolved and drawn to the
+ * Horizontal layout direction of this view is from Left to Right.
+ * Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_LTR = 0;
+
+ /**
+ * Horizontal layout direction of this view is from Right to Left.
+ * Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_RTL = 1;
+
+ /**
+ * Horizontal layout direction of this view is inherited from its parent.
+ * Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_INHERIT = 2;
+
+ /**
+ * Horizontal layout direction of this view is from deduced from the default language
+ * script for the locale. Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_LOCALE = 3;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+ * @hide
+ */
+ static final int LAYOUT_DIRECTION_MASK_SHIFT = 2;
+
+ /**
+ * Mask for use with private flags indicating bits used for horizontal layout direction.
+ * @hide
+ */
+ static final int LAYOUT_DIRECTION_MASK = 0x00000003 << LAYOUT_DIRECTION_MASK_SHIFT;
+
+ /**
+ * Indicates whether the view horizontal layout direction has been resolved and drawn to the
* right-to-left direction.
- *
* @hide
*/
- static final int LAYOUT_DIRECTION_RESOLVED_RTL = 0x00000004;
+ static final int LAYOUT_DIRECTION_RESOLVED_RTL = 4 << LAYOUT_DIRECTION_MASK_SHIFT;
/**
- * Indicates whether the view layout direction has been resolved.
- *
+ * Indicates whether the view horizontal layout direction has been resolved.
+ * @hide
+ */
+ static final int LAYOUT_DIRECTION_RESOLVED = 8 << LAYOUT_DIRECTION_MASK_SHIFT;
+
+ /**
+ * Mask for use with private flags indicating bits used for resolved horizontal layout direction.
* @hide
*/
- static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008;
+ static final int LAYOUT_DIRECTION_RESOLVED_MASK = 0x0000000C << LAYOUT_DIRECTION_MASK_SHIFT;
+
+ /*
+ * Array of horizontal layout direction flags for mapping attribute "layoutDirection" to correct
+ * flag value.
+ * @hide
+ */
+ private static final int[] LAYOUT_DIRECTION_FLAGS = {
+ LAYOUT_DIRECTION_LTR,
+ LAYOUT_DIRECTION_RTL,
+ LAYOUT_DIRECTION_INHERIT,
+ LAYOUT_DIRECTION_LOCALE
+ };
+
+ /**
+ * Default horizontal layout direction.
+ * @hide
+ */
+ private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
/**
@@ -1770,7 +1784,98 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*
* @hide
*/
- static final int HAS_TRANSIENT_STATE = 0x00000010;
+ static final int HAS_TRANSIENT_STATE = 0x00000100;
+
+
+ /**
+ * Text direction is inherited thru {@link ViewGroup}
+ */
+ public static final int TEXT_DIRECTION_INHERIT = 0;
+
+ /**
+ * Text direction is using "first strong algorithm". The first strong directional character
+ * determines the paragraph direction. If there is no strong directional character, the
+ * paragraph direction is the view's resolved layout direction.
+ */
+ public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
+
+ /**
+ * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains
+ * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
+ * If there are neither, the paragraph direction is the view's resolved layout direction.
+ */
+ public static final int TEXT_DIRECTION_ANY_RTL = 2;
+
+ /**
+ * Text direction is forced to LTR.
+ */
+ public static final int TEXT_DIRECTION_LTR = 3;
+
+ /**
+ * Text direction is forced to RTL.
+ */
+ public static final int TEXT_DIRECTION_RTL = 4;
+
+ /**
+ * Text direction is coming from the system Locale.
+ */
+ public static final int TEXT_DIRECTION_LOCALE = 5;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
+ * @hide
+ */
+ static final int TEXT_DIRECTION_MASK_SHIFT = 6;
+
+ /**
+ * Default text direction is inherited
+ */
+ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+
+ /**
+ * Mask for use with private flags indicating bits used for text direction.
+ * @hide
+ */
+ static final int TEXT_DIRECTION_MASK = 0x00000007 << TEXT_DIRECTION_MASK_SHIFT;
+
+ /**
+ * Array of text direction flags for mapping attribute "textDirection" to correct
+ * flag value.
+ * @hide
+ */
+ private static final int[] TEXT_DIRECTION_FLAGS = {
+ TEXT_DIRECTION_INHERIT << TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_ANY_RTL << TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_LTR << TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_RTL << TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_LOCALE << TEXT_DIRECTION_MASK_SHIFT
+ };
+
+ /**
+ * Indicates whether the view text direction has been resolved.
+ * @hide
+ */
+ static final int TEXT_DIRECTION_RESOLVED = 0x00000008 << TEXT_DIRECTION_MASK_SHIFT;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+ * @hide
+ */
+ static final int TEXT_DIRECTION_RESOLVED_MASK_SHIFT = 10;
+
+ /**
+ * Mask for use with private flags indicating bits used for resolved text direction.
+ * @hide
+ */
+ static final int TEXT_DIRECTION_RESOLVED_MASK = 0x00000007 << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
+
+ /**
+ * Indicates whether the view text direction has been resolved to the "first strong" heuristic.
+ * @hide
+ */
+ static final int TEXT_DIRECTION_RESOLVED_DEFAULT =
+ TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
/* End of masks for mPrivateFlags2 */
@@ -2647,82 +2752,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
AccessibilityDelegate mAccessibilityDelegate;
/**
- * Text direction is inherited thru {@link ViewGroup}
- */
- public static final int TEXT_DIRECTION_INHERIT = 0;
-
- /**
- * Text direction is using "first strong algorithm". The first strong directional character
- * determines the paragraph direction. If there is no strong directional character, the
- * paragraph direction is the view's resolved layout direction.
- *
- */
- public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
-
- /**
- * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains
- * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
- * If there are neither, the paragraph direction is the view's resolved layout direction.
- *
- */
- public static final int TEXT_DIRECTION_ANY_RTL = 2;
-
- /**
- * Text direction is forced to LTR.
- *
- */
- public static final int TEXT_DIRECTION_LTR = 3;
-
- /**
- * Text direction is forced to RTL.
- *
- */
- public static final int TEXT_DIRECTION_RTL = 4;
-
- /**
- * Text direction is coming from the system Locale.
- *
- */
- public static final int TEXT_DIRECTION_LOCALE = 5;
-
- /**
- * Default text direction is inherited
- *
- */
- protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
-
- /**
- * The text direction that has been defined by {@link #setTextDirection(int)}.
- *
- */
- @ViewDebug.ExportedProperty(category = "text", mapping = {
- @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
- })
- private int mTextDirection = DEFAULT_TEXT_DIRECTION;
-
- /**
- * The resolved text direction. This needs resolution if the value is
- * TEXT_DIRECTION_INHERIT. The resolution matches mTextDirection if it is
- * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
- * chain of the view.
- *
- */
- @ViewDebug.ExportedProperty(category = "text", mapping = {
- @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
- })
- private int mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
-
- /**
* Consistency verifier for debugging purposes.
* @hide
*/
@@ -2739,7 +2768,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public View(Context context) {
mContext = context;
mResources = context != null ? context.getResources() : null;
- mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
+ mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
+ // Set layout and text direction defaults
+ mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
+ (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = -1;
@@ -2949,17 +2981,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
break;
case com.android.internal.R.styleable.View_layoutDirection:
- // Clear any HORIZONTAL_DIRECTION flag already set
- viewFlagValues &= ~LAYOUT_DIRECTION_MASK;
- // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute
+ // Clear any layout direction flags (included resolved bits) already set
+ mPrivateFlags2 &= ~(LAYOUT_DIRECTION_MASK | LAYOUT_DIRECTION_RESOLVED_MASK);
+ // Set the layout direction flags depending on the value of the attribute
final int layoutDirection = a.getInt(attr, -1);
- if (layoutDirection != -1) {
- viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection];
- } else {
- // Set to default (LAYOUT_DIRECTION_INHERIT)
- viewFlagValues |= LAYOUT_DIRECTION_DEFAULT;
- }
- viewFlagMasks |= LAYOUT_DIRECTION_MASK;
+ final int value = (layoutDirection != -1) ?
+ LAYOUT_DIRECTION_FLAGS[layoutDirection] : LAYOUT_DIRECTION_DEFAULT;
+ mPrivateFlags2 |= (value << LAYOUT_DIRECTION_MASK_SHIFT);
break;
case com.android.internal.R.styleable.View_drawingCacheQuality:
final int cacheQuality = a.getInt(attr, 0);
@@ -3103,7 +3131,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);
break;
case R.styleable.View_textDirection:
- mTextDirection = a.getInt(attr, DEFAULT_TEXT_DIRECTION);
+ // Clear any text direction flag already set
+ mPrivateFlags2 &= ~TEXT_DIRECTION_MASK;
+ // Set the text direction flags depending on the value of the attribute
+ final int textDirection = a.getInt(attr, -1);
+ if (textDirection != -1) {
+ mPrivateFlags2 |= TEXT_DIRECTION_FLAGS[textDirection];
+ }
break;
}
}
@@ -4882,7 +4916,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE")
})
public int getLayoutDirection() {
- return mViewFlags & LAYOUT_DIRECTION_MASK;
+ return (mPrivateFlags2 & LAYOUT_DIRECTION_MASK) >> LAYOUT_DIRECTION_MASK_SHIFT;
}
/**
@@ -4899,9 +4933,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@RemotableViewMethod
public void setLayoutDirection(int layoutDirection) {
if (getLayoutDirection() != layoutDirection) {
+ // Reset the current layout direction and the resolved one
+ mPrivateFlags2 &= ~LAYOUT_DIRECTION_MASK;
resetResolvedLayoutDirection();
- // Setting the flag will also request a layout.
- setFlags(layoutDirection, LAYOUT_DIRECTION_MASK);
+ // Set the new layout direction (filtered) and ask for a layout pass
+ mPrivateFlags2 |=
+ ((layoutDirection << LAYOUT_DIRECTION_MASK_SHIFT) & LAYOUT_DIRECTION_MASK);
+ requestLayout();
}
}
@@ -4909,21 +4947,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* Returns the resolved layout direction for this view.
*
* @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
- * {@link #LAYOUT_DIRECTION_LTR} id the layout direction is not RTL.
+ * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
*/
@ViewDebug.ExportedProperty(category = "layout", mapping = {
- @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
- @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
public int getResolvedLayoutDirection() {
- resolveLayoutDirectionIfNeeded();
+ // The layout diretion will be resolved only if needed
+ if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) != LAYOUT_DIRECTION_RESOLVED) {
+ resolveLayoutDirection();
+ }
return ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED_RTL) == LAYOUT_DIRECTION_RESOLVED_RTL) ?
LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
/**
- * <p>Indicates whether or not this view's layout is right-to-left. This is resolved from
- * layout attribute and/or the inherited value from the parent.</p>
+ * Indicates whether or not this view's layout is right-to-left. This is resolved from
+ * layout attribute and/or the inherited value from the parent
*
* @return true if the layout is right-to-left.
*/
@@ -6940,10 +6981,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
mParent.recomputeViewAttributes(this);
}
}
-
- if ((changed & LAYOUT_DIRECTION_MASK) != 0) {
- requestLayout();
- }
}
/**
@@ -7364,7 +7401,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
invalidateViewProperty(false, false);
if (USE_DISPLAY_LIST_PROPERTIES && mDisplayList != null) {
- mDisplayList.setCameraDistance(distance);
+ mDisplayList.setCameraDistance(-Math.abs(distance) / dpi);
}
}
@@ -9797,7 +9834,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
jumpDrawablesToCurrentState();
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
- resolveLayoutDirectionIfNeeded();
+ resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
@@ -9828,31 +9865,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
/**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
+ * Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done.
*/
- private void resolveLayoutDirectionIfNeeded() {
- // Do not resolve if it is not needed
- if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) == LAYOUT_DIRECTION_RESOLVED) return;
-
+ public void resolveLayoutDirection() {
// Clear any previous layout direction resolution
- mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_RTL;
+ mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_MASK;
// Set resolved depending on layout direction
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- // We cannot do the resolution if there is no parent
- if (mParent == null) return;
-
// If this is root view, no need to look at parent's layout dir.
- if (mParent instanceof ViewGroup) {
+ if (canResolveLayoutDirection()) {
ViewGroup viewGroup = ((ViewGroup) mParent);
- // Check if the parent view group can resolve
- if (! viewGroup.canResolveLayoutDirection()) {
- return;
- }
if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
+ } else {
+ // Nothing to do, LTR by default
}
break;
case LAYOUT_DIRECTION_RTL:
@@ -9955,7 +9985,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public boolean canResolveLayoutDirection() {
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- return (mParent != null);
+ return (mParent != null) && (mParent instanceof ViewGroup);
default:
return true;
}
@@ -9966,8 +9996,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* when reset is done.
*/
public void resetResolvedLayoutDirection() {
- // Reset the current View resolution
- mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
+ // Reset the current resolved bits
+ mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_MASK;
onResolvedLayoutDirectionReset();
// Reset also the text direction
resetResolvedTextDirection();
@@ -10010,7 +10040,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
destroyDrawingCache();
- destroyLayer();
+ destroyLayer(false);
if (mAttachInfo != null) {
if (mDisplayList != null) {
@@ -10386,7 +10416,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
// Destroy any previous software drawing cache if needed
switch (mLayerType) {
case LAYER_TYPE_HARDWARE:
- destroyLayer();
+ destroyLayer(false);
// fall through - non-accelerated views may use software layer mechanism instead
case LAYER_TYPE_SOFTWARE:
destroyDrawingCache();
@@ -10524,11 +10554,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_HARDWARE
*/
- boolean destroyLayer() {
+ boolean destroyLayer(boolean valid) {
if (mHardwareLayer != null) {
AttachInfo info = mAttachInfo;
if (info != null && info.mHardwareRenderer != null &&
- info.mHardwareRenderer.isEnabled() && info.mHardwareRenderer.validate()) {
+ info.mHardwareRenderer.isEnabled() &&
+ (valid || info.mHardwareRenderer.validate())) {
mHardwareLayer.destroy();
mHardwareLayer = null;
@@ -10552,7 +10583,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @hide
*/
protected void destroyHardwareResources() {
- destroyLayer();
+ destroyLayer(true);
}
/**
@@ -10716,16 +10747,25 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
int layerType = (
!(mParent instanceof ViewGroup) || ((ViewGroup)mParent).mDrawLayers) ?
getLayerType() : LAYER_TYPE_NONE;
- if (!isLayer && layerType == LAYER_TYPE_HARDWARE && USE_DISPLAY_LIST_PROPERTIES) {
- final HardwareLayer layer = getHardwareLayer();
- if (layer != null && layer.isValid()) {
- canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ if (!isLayer && layerType != LAYER_TYPE_NONE && USE_DISPLAY_LIST_PROPERTIES) {
+ if (layerType == LAYER_TYPE_HARDWARE) {
+ final HardwareLayer layer = getHardwareLayer();
+ if (layer != null && layer.isValid()) {
+ canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ } else {
+ canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
+ Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ }
+ caching = true;
} else {
- canvas.saveLayer(0, 0,
- mRight - mLeft, mBottom - mTop, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ buildDrawingCache(true);
+ Bitmap cache = getDrawingCache(true);
+ if (cache != null) {
+ canvas.drawBitmap(cache, 0, 0, mLayerPaint);
+ caching = true;
+ }
}
- caching = true;
} else {
computeScroll();
@@ -11364,7 +11404,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
mTransformationInfo.mRotation, mTransformationInfo.mRotationX,
mTransformationInfo.mRotationY, mTransformationInfo.mScaleX,
mTransformationInfo.mScaleY);
- displayList.setCameraDistance(getCameraDistance());
+ if (mTransformationInfo.mCamera == null) {
+ mTransformationInfo.mCamera = new Camera();
+ mTransformationInfo.matrix3D = new Matrix();
+ }
+ displayList.setCameraDistance(mTransformationInfo.mCamera.getLocationZ());
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == PIVOT_EXPLICITLY_SET) {
displayList.setPivotX(getPivotX());
displayList.setPivotY(getPivotY());
@@ -11458,8 +11502,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
} else {
switch (layerType) {
case LAYER_TYPE_SOFTWARE:
- buildDrawingCache(true);
- cache = getDrawingCache(true);
+ if (useDisplayListProperties) {
+ hasDisplayList = canHaveDisplayList();
+ } else {
+ buildDrawingCache(true);
+ cache = getDrawingCache(true);
+ }
break;
case LAYER_TYPE_HARDWARE:
if (useDisplayListProperties) {
@@ -11481,7 +11529,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
layerType != LAYER_TYPE_HARDWARE;
int restoreTo = -1;
- if (!useDisplayListProperties) {
+ if (!useDisplayListProperties || transformToApply != null) {
restoreTo = canvas.save();
}
if (offsetForScroll) {
@@ -11515,11 +11563,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (concatMatrix) {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
- if (!useDisplayListProperties) {
- canvas.translate(-transX, -transY);
- canvas.concat(transformToApply.getMatrix());
- canvas.translate(transX, transY);
- }
+ canvas.translate(-transX, -transY);
+ canvas.concat(transformToApply.getMatrix());
+ canvas.translate(transX, transY);
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
@@ -11548,12 +11594,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
}
if (layerType == LAYER_TYPE_NONE) {
- if (!useDisplayListProperties) {
- final int scrollX = hasDisplayList ? 0 : sx;
- final int scrollY = hasDisplayList ? 0 : sy;
- canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
- scrollY + mBottom - mTop, multipliedAlpha, layerFlags);
- }
+ final int scrollX = hasDisplayList ? 0 : sx;
+ final int scrollY = hasDisplayList ? 0 : sy;
+ canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
+ scrollY + mBottom - mTop, multipliedAlpha, layerFlags);
}
} else {
// Alpha is handled by the child directly, clobber the layer's alpha
@@ -14487,8 +14531,16 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link #TEXT_DIRECTION_RTL},
* {@link #TEXT_DIRECTION_LOCALE},
*/
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
+ })
public int getTextDirection() {
- return mTextDirection;
+ return (mPrivateFlags2 & TEXT_DIRECTION_MASK) >> TEXT_DIRECTION_MASK_SHIFT;
}
/**
@@ -14504,17 +14556,26 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link #TEXT_DIRECTION_LOCALE},
*/
public void setTextDirection(int textDirection) {
- if (textDirection != mTextDirection) {
- mTextDirection = textDirection;
+ if (getTextDirection() != textDirection) {
+ // Reset the current text direction and the resolved one
+ mPrivateFlags2 &= ~TEXT_DIRECTION_MASK;
resetResolvedTextDirection();
+ // Set the new text direction
+ mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
requestLayout();
+ invalidate(true);
}
}
/**
* Return the resolved text direction.
*
- * @return the resolved text direction. Return one of:
+ * This needs resolution if the value is TEXT_DIRECTION_INHERIT. The resolution matches
+ * {@link #getTextDirection()}if it is not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds
+ * up the parent chain of the view. if there is no parent, then it will return the default
+ * {@link #TEXT_DIRECTION_FIRST_STRONG}.
+ *
+ * @return the resolved text direction. Returns one of:
*
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
@@ -14523,10 +14584,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link #TEXT_DIRECTION_LOCALE},
*/
public int getResolvedTextDirection() {
- if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
+ // The text direction will be resolved only if needed
+ if ((mPrivateFlags2 & TEXT_DIRECTION_RESOLVED) != TEXT_DIRECTION_RESOLVED) {
resolveTextDirection();
}
- return mResolvedTextDirection;
+ return (mPrivateFlags2 & TEXT_DIRECTION_RESOLVED_MASK) >> TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
}
/**
@@ -14534,17 +14596,51 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* resolution is done.
*/
public void resolveTextDirection() {
- if (mResolvedTextDirection != TEXT_DIRECTION_INHERIT) {
- // Resolution has already been done.
- return;
- }
- if (mTextDirection != TEXT_DIRECTION_INHERIT) {
- mResolvedTextDirection = mTextDirection;
- } else if (mParent != null && mParent instanceof ViewGroup) {
- mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
- } else {
- mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
+ // Reset any previous text direction resolution
+ mPrivateFlags2 &= ~(TEXT_DIRECTION_RESOLVED | TEXT_DIRECTION_RESOLVED_MASK);
+
+ // Set resolved text direction flag depending on text direction flag
+ final int textDirection = getTextDirection();
+ switch(textDirection) {
+ case TEXT_DIRECTION_INHERIT:
+ if (canResolveTextDirection()) {
+ ViewGroup viewGroup = ((ViewGroup) mParent);
+
+ // Set current resolved direction to the same value as the parent's one
+ final int parentResolvedDirection = viewGroup.getResolvedTextDirection();
+ switch (parentResolvedDirection) {
+ case TEXT_DIRECTION_FIRST_STRONG:
+ case TEXT_DIRECTION_ANY_RTL:
+ case TEXT_DIRECTION_LTR:
+ case TEXT_DIRECTION_RTL:
+ case TEXT_DIRECTION_LOCALE:
+ mPrivateFlags2 |=
+ (parentResolvedDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Default resolved direction is "first strong" heuristic
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
+ }
+ } else {
+ // We cannot do the resolution if there is no parent, so use the default one
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
+ }
+ break;
+ case TEXT_DIRECTION_FIRST_STRONG:
+ case TEXT_DIRECTION_ANY_RTL:
+ case TEXT_DIRECTION_LTR:
+ case TEXT_DIRECTION_RTL:
+ case TEXT_DIRECTION_LOCALE:
+ // Resolved direction is the same as text direction
+ mPrivateFlags2 |= (textDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Default resolved direction is "first strong" heuristic
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
}
+
+ // Set to resolved
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED;
onResolvedTextDirectionChanged();
}
@@ -14558,12 +14654,26 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
+ * Check if text direction resolution can be done.
+ *
+ * @return true if text direction resolution can be done otherwise return false.
+ */
+ public boolean canResolveTextDirection() {
+ switch (getTextDirection()) {
+ case TEXT_DIRECTION_INHERIT:
+ return (mParent != null) && (mParent instanceof ViewGroup);
+ default:
+ return true;
+ }
+ }
+
+ /**
* Reset resolved text direction. Text direction can be resolved with a call to
* getResolvedTextDirection(). Will call {@link View#onResolvedTextDirectionReset} when
* reset is done.
*/
public void resetResolvedTextDirection() {
- mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
+ mPrivateFlags2 &= ~(TEXT_DIRECTION_RESOLVED | TEXT_DIRECTION_RESOLVED_MASK);
onResolvedTextDirectionReset();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 42426b9..30d6ec7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1820,7 +1820,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* Resets the cancel next up flag.
* Returns true if the flag was previously set.
*/
- private boolean resetCancelNextUpFlag(View view) {
+ private static boolean resetCancelNextUpFlag(View view) {
if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
return true;
@@ -2679,15 +2679,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return child.draw(canvas, this, drawingTime);
}
- @Override
- public void requestLayout() {
- if (mChildrenCount > 0 && getAccessibilityNodeProvider() != null) {
- throw new IllegalStateException("Views with AccessibilityNodeProvider"
- + " can't have children.");
- }
- super.requestLayout();
- }
-
/**
*
* @param enabled True if children should be drawn with layers, false otherwise.
@@ -3109,11 +3100,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
- if (getAccessibilityNodeProvider() != null) {
- throw new IllegalStateException("Views with AccessibilityNodeProvider"
- + " can't have children.");
- }
-
if (mTransition != null) {
// Don't prevent other add transitions from completing, but cancel remove
// transitions to let them complete the process before we add to the container
@@ -3920,9 +3906,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
do {
if (parent instanceof ViewGroup) {
ViewGroup parentVG = (ViewGroup) parent;
- parent = parentVG.invalidateChildInParentFast(left, top, dirty);
- left = parentVG.mLeft;
- top = parentVG.mTop;
+ if (parentVG.mLayerType != LAYER_TYPE_NONE) {
+ // Layered parents should be recreated, not just re-issued
+ parentVG.invalidate();
+ parent = null;
+ } else {
+ parent = parentVG.invalidateChildInParentFast(left, top, dirty);
+ left = parentVG.mLeft;
+ top = parentVG.mTop;
+ }
} else {
// Reached the top; this calls into the usual invalidate method in
// ViewRootImpl, which schedules a traversal
@@ -4678,6 +4670,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public void clearDisappearingChildren() {
if (mDisappearingChildren != null) {
mDisappearingChildren.clear();
+ invalidate();
}
}
@@ -4789,7 +4782,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.mParent = null;
}
}
- mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+ invalidate();
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4eb70ab..befc1c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1809,7 +1809,7 @@ public final class ViewRootImpl implements ViewParent,
*
* @return The measure spec to use to measure the root view.
*/
- private int getRootMeasureSpec(int windowSize, int rootDimension) {
+ private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
@@ -1919,7 +1919,9 @@ public final class ViewRootImpl implements ViewParent,
}
private void performDraw() {
- if (!mAttachInfo.mScreenOn) return;
+ if (!mAttachInfo.mScreenOn && !mReportNextDraw) {
+ return;
+ }
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
@@ -2430,12 +2432,12 @@ public final class ViewRootImpl implements ViewParent,
mAccessibilityInteractionConnectionManager);
removeSendWindowContentChangedCallback();
+ destroyHardwareRenderer();
+
mView = null;
mAttachInfo.mRootView = null;
mAttachInfo.mSurface = null;
- destroyHardwareRenderer();
-
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
@@ -2891,7 +2893,7 @@ public final class ViewRootImpl implements ViewParent,
* @param focused The currently focused view.
* @return An appropriate view, or null if no such view exists.
*/
- private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
+ private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
ViewParent parent = focused.getParent();
while (parent instanceof ViewGroup) {
final ViewGroup vgParent = (ViewGroup) parent;
@@ -3761,7 +3763,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void getGfxInfo(View view, int[] info) {
+ private static void getGfxInfo(View view, int[] info) {
DisplayList displayList = view.mDisplayList;
info[0]++;
if (displayList != null) {
@@ -3782,6 +3784,7 @@ public final class ViewRootImpl implements ViewParent,
if (immediate) {
doDie();
} else {
+ destroyHardwareRenderer();
mHandler.sendEmptyMessage(MSG_DIE);
}
}
@@ -3824,10 +3827,18 @@ public final class ViewRootImpl implements ViewParent,
}
private void destroyHardwareRenderer() {
- if (mAttachInfo.mHardwareRenderer != null) {
- mAttachInfo.mHardwareRenderer.destroy(true);
- mAttachInfo.mHardwareRenderer = null;
- mAttachInfo.mHardwareAccelerated = false;
+ AttachInfo attachInfo = mAttachInfo;
+ HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
+
+ if (hardwareRenderer != null) {
+ if (mView != null) {
+ hardwareRenderer.destroyHardwareResources(mView);
+ }
+ hardwareRenderer.destroy(true);
+ hardwareRenderer.setRequested(false);
+
+ attachInfo.mHardwareRenderer = null;
+ attachInfo.mHardwareAccelerated = false;
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 0e4a30f..f2ee9f9 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -354,7 +354,7 @@ public class WindowManagerImpl implements WindowManager {
View removeViewLocked(int index) {
ViewRootImpl root = mRoots[index];
View view = root.getView();
-
+
// Don't really remove until we have matched all calls to add().
root.mAddNesting--;
if (root.mAddNesting > 0) {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index dc8c71b..d92ebcd 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -875,7 +875,7 @@ public abstract class Animation implements Cloneable {
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
- * @param outTransformation A tranformation object that is provided by the
+ * @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @param scale Scaling factor to apply to any inputs to the transform operation, such
* pivot points being rotated or scaled around.
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index cf210c8..e8c1d23 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -112,6 +112,16 @@ public class Transformation {
}
/**
+ * Like {@link #compose(Transformation)} but does this.postConcat(t) of
+ * the transformation matrix.
+ * @hide
+ */
+ public void postCompose(Transformation t) {
+ mAlpha *= t.getAlpha();
+ mMatrix.postConcat(t.getMatrix());
+ }
+
+ /**
* @return The 3x3 Matrix representing the trnasformation to apply to the
* coordinates of the object being animated
*/
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0e5ff20..17dbde8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -403,6 +403,9 @@ public final class InputMethodManager {
mIInputContext.finishComposingText();
} catch (RemoteException e) {
}
+ // Check focus again in case that "onWindowFocus" is called before
+ // handling this message.
+ checkFocus(mHasBeenInactive);
}
}
return;
@@ -1173,13 +1176,17 @@ public final class InputMethodManager {
}
}
+ private void checkFocus(boolean forceNewFocus) {
+ if (checkFocusNoStartInput(forceNewFocus)) {
+ startInputInner(null, 0, 0, 0);
+ }
+ }
+
/**
* @hide
*/
public void checkFocus() {
- if (checkFocusNoStartInput(false)) {
- startInputInner(null, 0, 0, 0);
- }
+ checkFocus(false);
}
private boolean checkFocusNoStartInput(boolean forceNewFocus) {
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 18dec52..c22750e 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.IllegalFormatException;
import java.util.List;
import java.util.Locale;
@@ -45,6 +46,9 @@ public final class InputMethodSubtype implements Parcelable {
private static final String TAG = InputMethodSubtype.class.getSimpleName();
private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
+ // TODO: remove this
+ private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
+ "UntranslatableReplacementStringInSubtypeName";
private final boolean mIsAuxiliary;
private final boolean mOverridesImplicitlyEnabledSubtype;
@@ -215,7 +219,17 @@ public final class InputMethodSubtype implements Parcelable {
final CharSequence subtypeName = context.getPackageManager().getText(
packageName, mSubtypeNameResId, appInfo);
if (!TextUtils.isEmpty(subtypeName)) {
- return String.format(subtypeName.toString(), localeStr);
+ final String replacementString =
+ containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
+ ? getExtraValueOf(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
+ : localeStr;
+ try {
+ return String.format(
+ subtypeName.toString(), replacementString != null ? replacementString : "");
+ } catch (IllegalFormatException e) {
+ Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
+ return "";
+ }
} else {
return localeStr;
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index dbcea71..72af251 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -448,7 +448,7 @@ class BrowserFrame extends Handler {
// loadType is not used yet
if (isMainFrame) {
mCommitted = true;
- mWebViewCore.getWebView().mViewManager.postResetStateAll();
+ mWebViewCore.getWebViewClassic().mViewManager.postResetStateAll();
}
}
@@ -910,7 +910,7 @@ class BrowserFrame extends Handler {
* Close this frame and window.
*/
private void closeWindow(WebViewCore w) {
- mCallbackProxy.onCloseWindow(w.getWebView());
+ mCallbackProxy.onCloseWindow(w.getWebViewClassic());
}
// XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index a21d3ee..349113e 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,12 +42,7 @@ class DebugFlags {
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
public static final boolean WEB_SYNC_MANAGER = false;
- public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- /*
- * Set to true to allow the WebTextView to draw on top of the web page in a
- * different color with no background so you can see how the two line up.
- */
- public static final boolean DRAW_WEBTEXTVIEW = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
}
diff --git a/core/java/android/webkit/FindListener.java b/core/java/android/webkit/FindListener.java
deleted file mode 100644
index 124f737..0000000
--- a/core/java/android/webkit/FindListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-/**
- * @hide
- */
-public interface FindListener {
- /**
- * Notify the host application that a find result is available.
- *
- * @param numberOfMatches How many matches have been found
- * @param activeMatchOrdinal The ordinal of the currently selected match
- * @param isDoneCounting Whether we have finished counting matches
- */
- public void onFindResultReceived(int numberOfMatches,
- int activeMatchOrdinal, boolean isDoneCounting);
-}
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index 8e1f573..689884f 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -183,7 +183,7 @@ class HTML5Audio extends Handler
resetMediaPlayer();
mContext = webViewCore.getContext();
mIsPrivateBrowsingEnabledGetter = new IsPrivateBrowsingEnabledGetter(
- webViewCore.getContext().getMainLooper(), webViewCore.getWebView());
+ webViewCore.getContext().getMainLooper(), webViewCore.getWebViewClassic());
}
private void resetMediaPlayer() {
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 40c3778..5fa4bad 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -724,7 +724,7 @@ class HTML5VideoViewProxy extends Handler
* @return a new HTML5VideoViewProxy object.
*/
public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
- return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
+ return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr);
}
/* package */ WebViewClassic getWebView() {
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 6850eea..c41bc00 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -121,9 +121,6 @@ public class WebSettingsClassic extends WebSettings {
private boolean mForceUserScalable = false;
// AutoFill Profile data
- /**
- * @hide for now, pending API council approval.
- */
public static class AutoFillProfile {
private int mUniqueId;
private String mFullName;
@@ -644,7 +641,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Set the double-tap zoom of the page in percent. Default is 100.
* @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
- * @hide
*/
public void setDoubleTapZoom(int doubleTapZoom) {
if (mDoubleTapZoom != doubleTapZoom) {
@@ -656,7 +652,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Get the double-tap zoom of the page in percent.
* @return A percent value describing the double-tap zoom.
- * @hide
*/
public int getDoubleTapZoom() {
return mDoubleTapZoom;
@@ -1012,7 +1007,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Set the number of pages cached by the WebKit for the history navigation.
* @param size A non-negative integer between 0 (no cache) and 20 (max).
- * @hide
*/
public synchronized void setPageCacheCapacity(int size) {
if (size < 0) size = 0;
@@ -1108,7 +1102,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Tell the WebView to use Skia's hardware accelerated rendering path
* @param flag True if the WebView should use Skia's hw-accel path
- * @hide
*/
public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
if (mHardwareAccelSkia != flag) {
@@ -1119,7 +1112,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* @return True if the WebView is using hardware accelerated skia
- * @hide
*/
public synchronized boolean getHardwareAccelSkiaEnabled() {
return mHardwareAccelSkia;
@@ -1128,7 +1120,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Tell the WebView to show the visual indicator
* @param flag True if the WebView should show the visual indicator
- * @hide
*/
public synchronized void setShowVisualIndicator(boolean flag) {
if (mShowVisualIndicator != flag) {
@@ -1139,7 +1130,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* @return True if the WebView is showing the visual indicator
- * @hide
*/
public synchronized boolean getShowVisualIndicator() {
return mShowVisualIndicator;
@@ -1283,7 +1273,6 @@ public class WebSettingsClassic extends WebSettings {
* @param flag True if the WebView should enable WebWorkers.
* Note that this flag only affects V8. JSC does not have
* an equivalent setting.
- * @hide
*/
public synchronized void setWorkersEnabled(boolean flag) {
if (mWorkersEnabled != flag) {
@@ -1305,8 +1294,8 @@ public class WebSettingsClassic extends WebSettings {
/**
* Sets whether XSS Auditor is enabled.
+ * Only used by LayoutTestController.
* @param flag Whether XSS Auditor should be enabled.
- * @hide Only used by LayoutTestController.
*/
public synchronized void setXSSAuditorEnabled(boolean flag) {
if (mXSSAuditorEnabled != flag) {
@@ -1507,7 +1496,6 @@ public class WebSettingsClassic extends WebSettings {
* of an HTML page to fit the screen. This conflicts with attempts by
* the UI to zoom in and out of an image, so it is set false by default.
* @param shrink Set true to let webkit shrink the standalone image to fit.
- * {@hide}
*/
public void setShrinksStandaloneImagesToFit(boolean shrink) {
if (mShrinksStandaloneImagesToFit != shrink) {
@@ -1520,7 +1508,6 @@ public class WebSettingsClassic extends WebSettings {
* Specify the maximum decoded image size. The default is
* 2 megs for small memory devices and 8 megs for large memory devices.
* @param size The maximum decoded size, or zero to set to the default.
- * @hide
*/
public void setMaximumDecodedImageSize(long size) {
if (mMaximumDecodedImageSize != size) {
@@ -1562,7 +1549,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Returns whether the viewport metatag can disable zooming
- * @hide
*/
public boolean forceUserScalable() {
return mForceUserScalable;
@@ -1571,7 +1557,6 @@ public class WebSettingsClassic extends WebSettings {
/**
* Sets whether viewport metatag can disable zooming.
* @param flag Whether or not to forceably enable user scalable.
- * @hide
*/
public synchronized void setForceUserScalable(boolean flag) {
mForceUserScalable = flag;
@@ -1584,9 +1569,6 @@ public class WebSettingsClassic extends WebSettings {
}
}
- /**
- * @hide
- */
public synchronized void setAutoFillEnabled(boolean enabled) {
// AutoFill is always disabled in private browsing mode.
boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
@@ -1596,16 +1578,10 @@ public class WebSettingsClassic extends WebSettings {
}
}
- /**
- * @hide
- */
public synchronized boolean getAutoFillEnabled() {
return mAutoFillEnabled;
}
- /**
- * @hide
- */
public synchronized void setAutoFillProfile(AutoFillProfile profile) {
if (mAutoFillProfile != profile) {
mAutoFillProfile = profile;
@@ -1613,9 +1589,6 @@ public class WebSettingsClassic extends WebSettings {
}
}
- /**
- * @hide
- */
public synchronized AutoFillProfile getAutoFillProfile() {
return mAutoFillProfile;
}
@@ -1633,18 +1606,12 @@ public class WebSettingsClassic extends WebSettings {
}
}
- /**
- * @hide
- */
public void setProperty(String key, String value) {
if (mWebView.nativeSetProperty(key, value)) {
- mWebView.contentInvalidateAll();
+ mWebView.invalidate();
}
}
- /**
- * @hide
- */
public String getProperty(String key) {
return mWebView.nativeGetProperty(key);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5e09416..d225594 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -311,6 +312,24 @@ public class WebView extends AbsoluteLayout
public static final String SCHEME_GEO = "geo:0,0?q=";
/**
+ * Interface to listen for find results.
+ * @hide
+ */
+ public interface FindListener {
+ /**
+ * Notify the listener about progress made by a find operation.
+ *
+ * @param numberOfMatches How many matches have been found.
+ * @param activeMatchOrdinal The zero-based ordinal of the currently selected match.
+ * @param isDoneCounting Whether the find operation has actually completed. The listener
+ * may be notified multiple times while the operation is underway, and the numberOfMatches
+ * value should not be considered final unless isDoneCounting is true.
+ */
+ public void onFindResultReceived(int numberOfMatches, int activeMatchOrdinal,
+ boolean isDoneCounting);
+ }
+
+ /**
* Interface to listen for new pictures as they change.
* @deprecated This interface is now obsolete.
*/
@@ -1227,10 +1246,10 @@ public class WebView extends AbsoluteLayout
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
+ * Register the listener to be notified as find-on-page operations progress.
+ * This will replace the current listener.
*
- * @param listener An implementation of FindListener
+ * @param listener An implementation of {@link WebView#FindListener}.
* @hide
*/
public void setFindListener(FindListener listener) {
@@ -1980,4 +1999,10 @@ public class WebView extends AbsoluteLayout
public void setBackgroundColor(int color) {
mProvider.getViewDelegate().setBackgroundColor(color);
}
+
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ super.setLayerType(layerType, paint);
+ mProvider.getViewDelegate().setLayerType(layerType, paint);
+ }
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index e5434ce..5ae2fe0 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -348,8 +348,6 @@ import java.util.regex.Pattern;
*
* @hide
*/
-// TODO: Remove duplicated API documentation and @hide from fields and methods, and
-// checkThread() call. (All left in for now to ease branch merging.)
// TODO: Check if any WebView published API methods are called from within here, and if so
// we should bounce the call out via the proxy to enable any sub-class to override it.
@Widget
@@ -1255,6 +1253,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
static final int AUTOFILL_FORM = 148;
static final int ANIMATE_TEXT_SCROLL = 149;
static final int EDIT_TEXT_SIZE_CHANGED = 150;
+ static final int SHOW_CARET_HANDLE = 151;
private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
@@ -1441,7 +1440,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private PictureListener mPictureListener;
// Used to notify listeners about find-on-page results.
- private FindListener mFindListener;
+ private WebView.FindListener mFindListener;
/**
* Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
@@ -1463,8 +1462,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
- checkThread();
-
Context context = mContext;
// Used by the chrome stack to find application paths
@@ -1496,8 +1493,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mEditTextScroller = new Scroller(context);
}
- // === START: WebView Proxy binding ===
- // Keep the webview proxy / SPI related stuff in this section, to minimize merge conflicts.
+ // WebViewProvider bindings
static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics {
@Override
@@ -1586,8 +1582,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mWebViewPrivate.setScrollYRaw(mScrollY);
}
- // === END: WebView Proxy binding ===
-
private static class TrustStorageListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1968,7 +1962,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setHorizontalScrollbarOverlay(boolean overlay) {
- checkThread();
mOverlayHorizontalScrollbar = overlay;
}
@@ -1977,7 +1970,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setVerticalScrollbarOverlay(boolean overlay) {
- checkThread();
mOverlayVerticalScrollbar = overlay;
}
@@ -1986,7 +1978,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean overlayHorizontalScrollbar() {
- checkThread();
return mOverlayHorizontalScrollbar;
}
@@ -1995,7 +1986,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean overlayVerticalScrollbar() {
- checkThread();
return mOverlayVerticalScrollbar;
}
@@ -2021,7 +2011,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Returns the height (in pixels) of the embedded title bar (if any). Does not care about
* scrolling
- * @hide
*/
protected int getTitleHeight() {
if (mWebView instanceof TitleBarDelegate) {
@@ -2038,7 +2027,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
public int getVisibleTitleHeight() {
// Actually, this method returns the height of the embedded title bar if one is set via the
// hidden setEmbeddedTitleBar method.
- checkThread();
return getVisibleTitleHeightImpl();
}
@@ -2084,7 +2072,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public SslCertificate getCertificate() {
- checkThread();
return mCertificate;
}
@@ -2093,7 +2080,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setCertificate(SslCertificate certificate) {
- checkThread();
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "setCertificate=" + certificate);
}
@@ -2110,7 +2096,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void savePassword(String host, String username, String password) {
- checkThread();
mDatabase.setUsernamePassword(host, username, password);
}
@@ -2120,7 +2105,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
- checkThread();
mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -2129,7 +2113,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public String[] getHttpAuthUsernamePassword(String host, String realm) {
- checkThread();
return mDatabase.getHttpAuthUsernamePassword(host, realm);
}
@@ -2169,7 +2152,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void destroy() {
- checkThread();
destroyImpl();
}
@@ -2202,7 +2184,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Deprecated
public static void enablePlatformNotifications() {
- checkThread();
synchronized (WebViewClassic.class) {
sNotificationsEnabled = true;
Context context = JniUtil.getContext();
@@ -2216,7 +2197,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Deprecated
public static void disablePlatformNotifications() {
- checkThread();
synchronized (WebViewClassic.class) {
sNotificationsEnabled = false;
Context context = JniUtil.getContext();
@@ -2230,10 +2210,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*
* @param flags JS engine flags in a String
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public void setJsFlags(String flags) {
- checkThread();
mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
}
@@ -2242,17 +2221,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setNetworkAvailable(boolean networkUp) {
- checkThread();
mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
networkUp ? 1 : 0, 0);
}
/**
* Inform WebView about the current network type.
- * {@hide}
*/
public void setNetworkType(String type, String subtype) {
- checkThread();
Map<String, String> map = new HashMap<String, String>();
map.put("type", type);
map.put("subtype", subtype);
@@ -2264,7 +2240,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public WebBackForwardList saveState(Bundle outState) {
- checkThread();
if (outState == null) {
return null;
}
@@ -2316,7 +2291,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
- checkThread();
if (dest == null || b == null) {
return false;
}
@@ -2378,7 +2352,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
@Deprecated
public boolean restorePicture(Bundle b, File src) {
- checkThread();
if (src == null || b == null) {
return false;
}
@@ -2424,7 +2397,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* of WebView.
* @param stream The {@link OutputStream} to save to
* @return True if saved successfully
- * @hide
*/
public boolean saveViewState(OutputStream stream) {
try {
@@ -2440,7 +2412,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* {@link #saveViewState(OutputStream)} for more information.
* @param stream The {@link InputStream} to load from
* @return True if loaded successfully
- * @hide
*/
public boolean loadViewState(InputStream stream) {
try {
@@ -2470,7 +2441,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public WebBackForwardList restoreState(Bundle inState) {
- checkThread();
WebBackForwardList returnList = null;
if (inState == null) {
return returnList;
@@ -2527,7 +2497,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
- checkThread();
loadUrlImpl(url, additionalHttpHeaders);
}
@@ -2545,7 +2514,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void loadUrl(String url) {
- checkThread();
loadUrlImpl(url);
}
@@ -2561,7 +2529,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void postUrl(String url, byte[] postData) {
- checkThread();
if (URLUtil.isNetworkUrl(url)) {
switchOutDrawHistory();
WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
@@ -2579,7 +2546,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void loadData(String data, String mimeType, String encoding) {
- checkThread();
loadDataImpl(data, mimeType, encoding);
}
@@ -2600,7 +2566,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
- checkThread();
if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
loadDataImpl(data, mimeType, encoding);
@@ -2622,7 +2587,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void saveWebArchive(String filename) {
- checkThread();
saveWebArchiveImpl(filename, false, null);
}
@@ -2644,7 +2608,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
- checkThread();
saveWebArchiveImpl(basename, autoname, callback);
}
@@ -2659,7 +2622,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void stopLoading() {
- checkThread();
// TODO: should we clear all the messages in the queue before sending
// STOP_LOADING?
switchOutDrawHistory();
@@ -2671,7 +2633,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void reload() {
- checkThread();
clearHelpers();
switchOutDrawHistory();
mWebViewCore.sendMessage(EventHub.RELOAD);
@@ -2682,7 +2643,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean canGoBack() {
- checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -2698,7 +2658,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void goBack() {
- checkThread();
goBackOrForwardImpl(-1);
}
@@ -2707,7 +2666,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean canGoForward() {
- checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -2723,7 +2681,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void goForward() {
- checkThread();
goBackOrForwardImpl(1);
}
@@ -2732,7 +2689,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean canGoBackOrForward(int steps) {
- checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -2749,7 +2705,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void goBackOrForward(int steps) {
- checkThread();
goBackOrForwardImpl(steps);
}
@@ -2770,7 +2725,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean isPrivateBrowsingEnabled() {
- checkThread();
return getSettings().isPrivateBrowsingEnabled();
}
@@ -2792,7 +2746,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean pageUp(boolean top) {
- checkThread();
if (mNativeClass == 0) {
return false;
}
@@ -2817,7 +2770,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean pageDown(boolean bottom) {
- checkThread();
if (mNativeClass == 0) {
return false;
}
@@ -2841,7 +2793,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearView() {
- checkThread();
mContentWidth = 0;
mContentHeight = 0;
setBaseLayer(0, null, false, false);
@@ -2853,7 +2804,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public Picture capturePicture() {
- checkThread();
if (mNativeClass == 0) return null;
Picture result = new Picture();
nativeCopyBaseContentToPicture(result);
@@ -2865,7 +2815,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public float getScale() {
- checkThread();
return mZoomManager.getScale();
}
@@ -2883,7 +2832,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setInitialScale(int scaleInPercent) {
- checkThread();
mZoomManager.setInitialScaleInPercent(scaleInPercent);
}
@@ -2892,7 +2840,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void invokeZoomPicker() {
- checkThread();
if (!getSettings().supportZoom()) {
Log.w(LOGTAG, "This WebView doesn't support zoom.");
return;
@@ -2906,7 +2853,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public HitTestResult getHitTestResult() {
- checkThread();
return mInitialHitTestResult;
}
@@ -2942,7 +2888,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void requestFocusNodeHref(Message hrefMsg) {
- checkThread();
if (hrefMsg == null) {
return;
}
@@ -2965,7 +2910,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void requestImageRef(Message msg) {
- checkThread();
if (0 == mNativeClass) return; // client isn't initialized
String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null;
Bundle data = msg.getData();
@@ -3019,7 +2963,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* along with it vertically, while remaining in view horizontally. Pass
* null to remove the title bar from the WebView, and return to drawing
* the WebView normally without translating to account for the title bar.
- * @hide
*/
public void setEmbeddedTitleBar(View v) {
if (mWebView instanceof TitleBarDelegate) {
@@ -3041,7 +2984,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Set where to render the embedded title bar
* NO_GRAVITY at the top of the page
* TOP at the top of the screen
- * @hide
*/
public void setTitleBarGravity(int gravity) {
mTitleGravity = gravity;
@@ -3421,7 +3363,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return getViewHeight();
}
- /** @hide */
@Override
public void onDrawVerticalScrollBar(Canvas canvas,
Drawable scrollBar,
@@ -3470,7 +3411,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public String getUrl() {
- checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getUrl() : null;
}
@@ -3480,7 +3420,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public String getOriginalUrl() {
- checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getOriginalUrl() : null;
}
@@ -3490,7 +3429,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public String getTitle() {
- checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getTitle() : null;
}
@@ -3500,7 +3438,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public Bitmap getFavicon() {
- checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getFavicon() : null;
}
@@ -3519,7 +3456,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public int getProgress() {
- checkThread();
return mCallbackProxy.getProgress();
}
@@ -3528,7 +3464,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public int getContentHeight() {
- checkThread();
return mContentHeight;
}
@@ -3540,9 +3475,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return mContentWidth;
}
- /**
- * @hide
- */
public int getPageBackgroundColor() {
return nativeGetBackgroundColor();
}
@@ -3552,7 +3484,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void pauseTimers() {
- checkThread();
mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
}
@@ -3561,7 +3492,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void resumeTimers() {
- checkThread();
mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
}
@@ -3570,7 +3500,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void onPause() {
- checkThread();
if (!mIsPaused) {
mIsPaused = true;
mWebViewCore.sendMessage(EventHub.ON_PAUSE);
@@ -3609,7 +3538,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void onResume() {
- checkThread();
if (mIsPaused) {
mIsPaused = false;
mWebViewCore.sendMessage(EventHub.ON_RESUME);
@@ -3641,7 +3569,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void freeMemory() {
- checkThread();
mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
}
@@ -3650,7 +3577,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearCache(boolean includeDiskFiles) {
- checkThread();
// Note: this really needs to be a static method as it clears cache for all
// WebView. But we need mWebViewCore to send message to WebCore thread, so
// we can't make this static.
@@ -3663,7 +3589,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearFormData() {
- checkThread();
if (mAutoCompletePopup != null) {
mAutoCompletePopup.clearAdapter();
}
@@ -3674,7 +3599,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearHistory() {
- checkThread();
mCallbackProxy.getBackForwardList().setClearPending();
mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
}
@@ -3684,7 +3608,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearSslPreferences() {
- checkThread();
mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
}
@@ -3693,18 +3616,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public WebBackForwardList copyBackForwardList() {
- checkThread();
return mCallbackProxy.getBackForwardList().clone();
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
- *
- * @param listener An implementation of FindListener
+ * See {@link WebView#setFindListener(WebView.FindListener)}.
+ * @hide
*/
- public void setFindListener(FindListener listener) {
- checkThread();
+ public void setFindListener(WebView.FindListener listener) {
mFindListener = listener;
}
@@ -3713,7 +3632,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void findNext(boolean forward) {
- checkThread();
if (0 == mNativeClass) return; // client isn't initialized
mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
}
@@ -3726,15 +3644,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return findAllBody(find, false);
}
- /**
- * @hide
- */
public void findAllAsync(String find) {
findAllBody(find, true);
}
private int findAllBody(String find, boolean isAsync) {
- checkThread();
if (0 == mNativeClass) return 0; // client isn't initialized
mLastFind = find;
if (find == null) return 0;
@@ -3771,7 +3685,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* @return boolean True if the find dialog is shown, false otherwise.
*/
public boolean showFindDialog(String text, boolean showIme) {
- checkThread();
FindActionModeCallback callback = new FindActionModeCallback(mContext);
if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
// Could not start the action mode, so end Find on page
@@ -3840,12 +3753,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* @return the address, or if no address is found, return null.
*/
public static String findAddress(String addr) {
- checkThread();
return findAddress(addr, false);
}
/**
- * @hide
* Return the first substring consisting of the address of a physical
* location. Currently, only addresses in the United States are detected,
* and consist of:
@@ -3875,7 +3786,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void clearMatches() {
- checkThread();
if (mNativeClass == 0)
return;
mWebViewCore.removeMessages(EventHub.FIND_ALL);
@@ -3905,7 +3815,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void documentHasImages(Message response) {
- checkThread();
if (response == null) {
return;
}
@@ -3914,8 +3823,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Request the scroller to abort any ongoing animation
- *
- * @hide
*/
public void stopScroll() {
mScroller.forceFinished(true);
@@ -4339,7 +4246,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setWebViewClient(WebViewClient client) {
- checkThread();
mCallbackProxy.setWebViewClient(client);
}
@@ -4347,7 +4253,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Gets the WebViewClient
* @return the current WebViewClient instance.
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public WebViewClient getWebViewClient() {
return mCallbackProxy.getWebViewClient();
@@ -4358,7 +4264,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setDownloadListener(DownloadListener listener) {
- checkThread();
mCallbackProxy.setDownloadListener(listener);
}
@@ -4367,7 +4272,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void setWebChromeClient(WebChromeClient client) {
- checkThread();
mCallbackProxy.setWebChromeClient(client);
}
@@ -4375,7 +4279,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Gets the chrome handler.
* @return the current WebChromeClient instance.
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public WebChromeClient getWebChromeClient() {
return mCallbackProxy.getWebChromeClient();
@@ -4386,7 +4290,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* WebBackForwardListClient for handling new items and changes in the
* history index.
* @param client An implementation of WebBackForwardListClient.
- * {@hide}
*/
public void setWebBackForwardListClient(WebBackForwardListClient client) {
mCallbackProxy.setWebBackForwardListClient(client);
@@ -4394,7 +4297,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Gets the WebBackForwardListClient.
- * {@hide}
*/
public WebBackForwardListClient getWebBackForwardListClient() {
return mCallbackProxy.getWebBackForwardListClient();
@@ -4406,21 +4308,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
@Deprecated
public void setPictureListener(PictureListener listener) {
- checkThread();
mPictureListener = listener;
}
- /**
- * {@hide}
- */
/* FIXME: Debug only! Remove for SDK! */
public void externalRepresentation(Message callback) {
mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
}
- /**
- * {@hide}
- */
/* FIXME: Debug only! Remove for SDK! */
public void documentAsText(Message callback) {
mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
@@ -4431,7 +4326,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void addJavascriptInterface(Object object, String name) {
- checkThread();
if (object == null) {
return;
}
@@ -4446,7 +4340,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void removeJavascriptInterface(String interfaceName) {
- checkThread();
if (mWebViewCore != null) {
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mInterfaceName = interfaceName;
@@ -4461,7 +4354,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public WebSettingsClassic getSettings() {
- checkThread();
return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
}
@@ -4470,7 +4362,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Deprecated
public static synchronized PluginList getPluginList() {
- checkThread();
return new PluginList();
}
@@ -4479,7 +4370,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Deprecated
public void refreshPlugins(boolean reloadOpenPages) {
- checkThread();
}
//-------------------------------------------------------------------------
@@ -4783,7 +4673,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Select the word at the last click point.
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public boolean selectText() {
int x = viewToContentX(mLastTouchX + getScrollX());
@@ -4827,10 +4717,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
showVisualIndicator, isPictureAfterFirstLayout);
- if (layer == 0 || isPictureAfterFirstLayout) {
- mWebViewCore.resumeWebKitDraw();
- } else if (queueFull) {
+ if (queueFull) {
mWebViewCore.pauseWebKitDraw();
+ } else {
+ mWebViewCore.resumeWebKitDraw();
}
if (mHTML5VideoViewProxy != null) {
@@ -4923,7 +4813,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* startX, startY, endX, endY
*/
private void getSelectionHandles(int[] handles) {
- handles[0] = mSelectCursorBase.right;
+ handles[0] = mSelectCursorBase.left;
handles[1] = mSelectCursorBase.bottom;
handles[2] = mSelectCursorExtent.left;
handles[3] = mSelectCursorExtent.bottom;
@@ -5134,7 +5024,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Dump the display tree to "/sdcard/displayTree.txt"
*
- * @hide debug only
+ * debug only
*/
public void dumpDisplayTree() {
nativeDumpDisplayTree(getUrl());
@@ -5144,7 +5034,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
* "/sdcard/domTree.txt"
*
- * @hide debug only
+ * debug only
*/
public void dumpDomTree(boolean toFile) {
mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
@@ -5154,7 +5044,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Dump the render tree to adb shell if "toFile" is False, otherwise dump it
* to "/sdcard/renderTree.txt"
*
- * @hide debug only
+ * debug only
*/
public void dumpRenderTree(boolean toFile) {
mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
@@ -5163,7 +5053,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Called by DRT on UI thread, need to proxy to WebCore thread.
*
- * @hide debug only
+ * debug only
*/
public void useMockDeviceOrientation() {
mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
@@ -5172,7 +5062,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Called by DRT on WebCore thread.
*
- * @hide debug only
+ * debug only
*/
public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
@@ -5468,9 +5358,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private boolean setupWebkitSelect() {
syncSelectionCursors();
- if (mIsCaretSelection) {
- showPasteWindow();
- } else if (!startSelectActionMode()) {
+ if (!mIsCaretSelection && !startSelectActionMode()) {
selectionDone();
return false;
}
@@ -5511,13 +5399,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
@Deprecated
public void emulateShiftHeld() {
- checkThread();
}
/**
* Select all of the text in this WebView.
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public void selectAll() {
mWebViewCore.sendMessage(EventHub.SELECT_ALL);
@@ -5539,7 +5426,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (!mIsCaretSelection) {
updateWebkitSelection();
}
- mIsCaretSelection = false;
invalidate(); // redraw without selection
mAutoScrollX = 0;
mAutoScrollY = 0;
@@ -5550,7 +5436,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Copy the selection to the clipboard
*
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public boolean copySelection() {
boolean copiedSomething = false;
@@ -5577,7 +5463,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Cut the selected text into the clipboard
*
- * @hide This is an implementation detail
+ * This is an implementation detail
*/
public void cutSelection() {
copySelection();
@@ -5589,7 +5475,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Paste text from the clipboard to the cursor position.
*
- * @hide This is an implementation detail
+ * This is an implementation detail
*/
public void pasteFromClipboard() {
ClipboardManager cm = (ClipboardManager)mContext
@@ -5605,7 +5491,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * @hide This is an implementation detail.
+ * This is an implementation detail.
*/
public SearchBox getSearchBox() {
if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
@@ -5638,6 +5524,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
addAccessibilityApisToJavaScript();
mTouchEventQueue.reset();
+ updateHwAccelerated();
}
@Override
@@ -5657,6 +5544,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
removeAccessibilityApisFromJavaScript();
+ updateHwAccelerated();
}
@Override
@@ -5779,9 +5667,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mVisibleContentRect, getScale());
}
- /**
- * @hide
- */
@Override
public boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
@@ -6221,6 +6106,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
ted.mReprocess = mDeferTouchProcess;
ted.mNativeLayer = mCurrentScrollingLayerId;
ted.mNativeLayerRect.set(mScrollingLayerRect);
+ ted.mMotionEvent = MotionEvent.obtain(ev);
ted.mSequence = mTouchEventQueue.nextTouchSequence();
mTouchEventQueue.preQueueTouchEventData(ted);
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
@@ -6375,8 +6261,15 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
case MotionEvent.ACTION_UP: {
mGestureDetector.onTouchEvent(ev);
if (mTouchInEditText && mConfirmMove) {
+ stopTouch();
break; // We've been scrolling the edit text.
}
+ if (!mConfirmMove && mIsEditingText && mSelectionStarted &&
+ mIsCaretSelection) {
+ showPasteWindow();
+ stopTouch();
+ break;
+ }
// pass the touch events from UI thread to WebCore thread
if (shouldForwardTouchEvent()) {
TouchEventData ted = new TouchEventData();
@@ -6762,7 +6655,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
syncSelectionCursors();
if (mIsCaretSelection) {
resetCaretTimer();
- showPasteWindow();
}
invalidate();
}
@@ -6854,7 +6746,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private DrawData mLoadedPicture;
public void setMapTrackballToArrowKeys(boolean setMap) {
- checkThread();
mMapTrackballToArrowKeys = setMap;
}
@@ -7081,7 +6972,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
public void flingScroll(int vx, int vy) {
- checkThread();
mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0,
computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
invalidate();
@@ -7203,7 +7093,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
@Override
@Deprecated
public View getZoomControls() {
- checkThread();
if (!getSettings().supportZoom()) {
Log.w(LOGTAG, "This WebView doesn't support zoom.");
return null;
@@ -7232,7 +7121,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean canZoomIn() {
- checkThread();
return mZoomManager.canZoomIn();
}
@@ -7241,7 +7129,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean canZoomOut() {
- checkThread();
return mZoomManager.canZoomOut();
}
@@ -7250,7 +7137,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean zoomIn() {
- checkThread();
return mZoomManager.zoomIn();
}
@@ -7259,7 +7145,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public boolean zoomOut() {
- checkThread();
return mZoomManager.zoomOut();
}
@@ -7296,10 +7181,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
}
- void sendPluginDrawMsg() {
- mWebViewCore.sendMessage(EventHub.PLUGIN_SURFACE_READY);
- }
-
/*
* Return true if the rect (e.g. plugin) is fully visible and maximized
* inside the WebView.
@@ -7553,9 +7434,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE, null, 1000);
}
- /**
- * @hide
- */
public synchronized WebViewCore getWebViewCore() {
return mWebViewCore;
}
@@ -8525,6 +8403,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
break;
+ case SHOW_CARET_HANDLE:
+ if (!mSelectingText && mIsEditingText && mIsCaretSelection) {
+ setupWebkitSelect();
+ resetCaretTimer();
+ showPasteWindow();
+ }
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -8729,9 +8615,19 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
void onPageSwapOccurred(boolean notifyAnimationStarted);
}
- /** @hide Called by JNI when pages are swapped (only occurs with hardware
+ long mLastSwapTime;
+ double mAverageSwapFps;
+
+ /** Called by JNI when pages are swapped (only occurs with hardware
* acceleration) */
protected void pageSwapCallback(boolean notifyAnimationStarted) {
+ if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
+ long now = System.currentTimeMillis();
+ long diff = now - mLastSwapTime;
+ mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
+ Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
+ mLastSwapTime = now;
+ }
mWebViewCore.resumeWebKitDraw();
if (notifyAnimationStarted) {
mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
@@ -8793,7 +8689,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
}
- invalidateContentRect(draw.mInvalRegion.getBounds());
+ Rect invalBounds = draw.mInvalRegion.getBounds();
+ if (!invalBounds.isEmpty()) {
+ invalidateContentRect(invalBounds);
+ } else {
+ mWebView.invalidate();
+ }
if (mPictureListener != null) {
mPictureListener.onNewPicture(getWebView(), capturePicture());
@@ -8824,13 +8725,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
(data.mStart != data.mEnd ||
(mFieldPointer == nodePointer && mFieldPointer != 0))) {
mIsCaretSelection = (data.mStart == data.mEnd);
- if (!mSelectingText) {
- setupWebkitSelect();
- } else if (!mSelectionStarted) {
- syncSelectionCursors();
- }
- if (mIsCaretSelection) {
- resetCaretTimer();
+ if (mIsCaretSelection &&
+ (mInputConnection == null ||
+ mInputConnection.getEditable().length() == 0)) {
+ // There's no text, don't show caret handle.
+ selectionDone();
+ } else {
+ if (!mSelectingText) {
+ setupWebkitSelect();
+ } else if (!mSelectionStarted) {
+ syncSelectionCursors();
+ }
+ if (mIsCaretSelection) {
+ resetCaretTimer();
+ }
}
} else {
selectionDone();
@@ -9112,8 +9020,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// after the page regains focus.
mListBoxMessage = Message.obtain(null,
EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
- mListBoxDialog.dismiss();
- mListBoxDialog = null;
+ if (mListBoxDialog != null) {
+ mListBoxDialog.dismiss();
+ mListBoxDialog = null;
+ }
}
});
if (mSelection != -1) {
@@ -9288,7 +9198,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* view-specific zoom, scroll offset, or other changes. It does not draw
* any view-specific chrome, such as progress or URL bars.
*
- * @hide only needs to be accessible to Browser and testing
+ * only needs to be accessible to Browser and testing
*/
public void drawPage(Canvas canvas) {
calcOurContentVisibleRectF(mVisibleContentRect);
@@ -9298,7 +9208,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Enable the communication b/t the webView and VideoViewProxy
*
- * @hide only used by the Browser
+ * only used by the Browser
*/
public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
mHTML5VideoViewProxy = proxy;
@@ -9308,7 +9218,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* Set the time to wait between passing touches to WebCore. See also the
* TOUCH_SENT_INTERVAL member for further discussion.
*
- * @hide This is only used by the DRT test application.
+ * This is only used by the DRT test application.
*/
public void setTouchInterval(int interval) {
mCurrentTouchInterval = interval;
@@ -9335,34 +9245,46 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return mViewManager;
}
- private static void checkThread() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- Throwable throwable = new Throwable(
- "Warning: A WebView method was called on thread '" +
- Thread.currentThread().getName() + "'. " +
- "All WebView methods must be called on the UI thread. " +
- "Future versions of WebView may not support use on other threads.");
- Log.w(LOGTAG, Log.getStackTraceString(throwable));
- StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
- }
- }
-
- /** @hide send content invalidate */
+ /** send content invalidate */
protected void contentInvalidateAll() {
if (mWebViewCore != null && !mBlockWebkitViewMessages) {
mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
}
}
- /** @hide discard all textures from tiles. Used in Profiled WebView */
+ /** discard all textures from tiles. Used in Profiled WebView */
public void discardAllTextures() {
nativeDiscardAllTextures();
}
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ updateHwAccelerated();
+ }
+
+ private void updateHwAccelerated() {
+ if (mNativeClass == 0) {
+ return;
+ }
+ boolean hwAccelerated = false;
+ if (mWebView.isHardwareAccelerated()
+ && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
+ hwAccelerated = true;
+ }
+ int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated);
+ if (mWebViewCore == null || mBlockWebkitViewMessages) {
+ return;
+ }
+ if (result == 1) {
+ // Sync layers
+ mWebViewCore.layersDraw();
+ }
+ }
+
/**
* Begin collecting per-tile profiling data
*
- * @hide only used by profiling tests
+ * only used by profiling tests
*/
public void tileProfilingStart() {
nativeTileProfilingStart();
@@ -9370,29 +9292,29 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Return per-tile profiling data
*
- * @hide only used by profiling tests
+ * only used by profiling tests
*/
public float tileProfilingStop() {
return nativeTileProfilingStop();
}
- /** @hide only used by profiling tests */
+ /** only used by profiling tests */
public void tileProfilingClear() {
nativeTileProfilingClear();
}
- /** @hide only used by profiling tests */
+ /** only used by profiling tests */
public int tileProfilingNumFrames() {
return nativeTileProfilingNumFrames();
}
- /** @hide only used by profiling tests */
+ /** only used by profiling tests */
public int tileProfilingNumTilesInFrame(int frame) {
return nativeTileProfilingNumTilesInFrame(frame);
}
- /** @hide only used by profiling tests */
+ /** only used by profiling tests */
public int tileProfilingGetInt(int frame, int tile, String key) {
return nativeTileProfilingGetInt(frame, tile, key);
}
- /** @hide only used by profiling tests */
+ /** only used by profiling tests */
public float tileProfilingGetFloat(int frame, int tile, String key) {
return nativeTileProfilingGetFloat(frame, tile, key);
}
@@ -9480,4 +9402,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private static native boolean nativeIsBaseFirst(int instance);
private static native void nativeMapLayerRect(int instance, int layerId,
Rect rect);
+ // Returns 1 if a layer sync is needed, else 0
+ private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index d784b08..afb2992 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -71,9 +71,8 @@ public final class WebViewCore {
* WebViewCore always executes in the same thread as the native webkit.
*/
- // The WebView that corresponds to this WebViewCore.
- // TODO: rename this field (and its getter) to mWebViewClassic or mWebViewImpl.
- private WebViewClassic mWebView;
+ // The WebViewClassic that corresponds to this WebViewCore.
+ private WebViewClassic mWebViewClassic;
// Proxy for handling callbacks from native code
private final CallbackProxy mCallbackProxy;
// Settings object for maintaining all settings
@@ -149,7 +148,7 @@ public final class WebViewCore {
Map<String, Object> javascriptInterfaces) {
// No need to assign this in the WebCore thread.
mCallbackProxy = proxy;
- mWebView = w;
+ mWebViewClassic = w;
mJavascriptInterfaces = javascriptInterfaces;
// This context object is used to initialize the WebViewCore during
// subwindow creation.
@@ -182,7 +181,7 @@ public final class WebViewCore {
// ready.
mEventHub = new EventHub();
// Create a WebSettings object for maintaining all settings
- mSettings = new WebSettingsClassic(mContext, mWebView);
+ mSettings = new WebSettingsClassic(mContext, mWebViewClassic);
// The WebIconDatabase needs to be initialized within the UI thread so
// just request the instance here.
WebIconDatabase.getInstance();
@@ -235,8 +234,8 @@ public final class WebViewCore {
// Send a message back to WebView to tell it that we have set up the
// WebCore thread.
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.WEBCORE_INITIALIZED_MSG_ID,
mNativeClass, 0).sendToTarget();
}
@@ -331,8 +330,8 @@ public final class WebViewCore {
* @param nodePointer The node which just blurred.
*/
private void formDidBlur(int nodePointer) {
- if (mWebView == null) return;
- Message.obtain(mWebView.mPrivateHandler, WebViewClassic.FORM_DID_BLUR,
+ if (mWebViewClassic == null) return;
+ Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.FORM_DID_BLUR,
nodePointer, 0).sendToTarget();
}
@@ -340,8 +339,8 @@ public final class WebViewCore {
* Called by JNI when the focus node changed.
*/
private void focusNodeChanged(int nodePointer, WebKitHitTest hitTest) {
- if (mWebView == null) return;
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.FOCUS_NODE_CHANGED,
+ if (mWebViewClassic == null) return;
+ mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.FOCUS_NODE_CHANGED,
nodePointer, 0, hitTest).sendToTarget();
}
@@ -349,8 +348,8 @@ public final class WebViewCore {
* Called by JNI to advance focus to the next view.
*/
private void chromeTakeFocus(int webkitDirection) {
- if (mWebView == null) return;
- Message m = mWebView.mPrivateHandler.obtainMessage(
+ if (mWebViewClassic == null) return;
+ Message m = mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.TAKE_FOCUS);
m.arg1 = mapDirection(webkitDirection);
m.sendToTarget();
@@ -561,8 +560,8 @@ public final class WebViewCore {
* Notify the webview that we want to display the video layer fullscreen.
*/
protected void enterFullscreenForVideoLayer(int layerId, String url) {
- if (mWebView == null) return;
- Message message = Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic == null) return;
+ Message message = Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.ENTER_FULLSCREEN_VIDEO, layerId, 0);
message.obj = url;
message.sendToTarget();
@@ -573,8 +572,8 @@ public final class WebViewCore {
* This is called through JNI by webcore.
*/
protected void exitFullscreenVideo() {
- if (mWebView == null) return;
- Message message = Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic == null) return;
+ Message message = Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.EXIT_FULLSCREEN_VIDEO);
message.sendToTarget();
}
@@ -1256,7 +1255,7 @@ public final class WebViewCore {
return;
}
- if (mWebView == null || mNativeClass == 0) {
+ if (mWebViewClassic == null || mNativeClass == 0) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.w(LOGTAG, "Rejecting message " + msg.what
+ " because we are destroyed");
@@ -1294,7 +1293,7 @@ public final class WebViewCore {
mBrowserFrame = null;
mSettings.onDestroyed();
mNativeClass = 0;
- mWebView = null;
+ mWebViewClassic = null;
}
break;
@@ -1537,7 +1536,7 @@ public final class WebViewCore {
yArray, count, ted.mActionIndex,
ted.mMetaState);
Message.obtain(
- mWebView.mPrivateHandler,
+ mWebViewClassic.mPrivateHandler,
WebViewClassic.PREVENT_TOUCH_ID,
ted.mAction,
ted.mNativeResult ? 1 : 0,
@@ -1607,7 +1606,7 @@ public final class WebViewCore {
String modifiedSelectionString =
nativeModifySelection(mNativeClass, msg.arg1,
msg.arg2);
- mWebView.mPrivateHandler.obtainMessage(
+ mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.SELECTION_STRING_CHANGED,
modifiedSelectionString).sendToTarget();
break;
@@ -1653,7 +1652,7 @@ public final class WebViewCore {
(WebViewClassic.SaveWebArchiveMessage)msg.obj;
saveMessage.mResultFile =
saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
- mWebView.mPrivateHandler.obtainMessage(
+ mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
break;
@@ -1666,7 +1665,7 @@ public final class WebViewCore {
case SPLIT_PICTURE_SET:
nativeSplitContent(mNativeClass, msg.arg1);
- mWebView.mPrivateHandler.obtainMessage(
+ mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.REPLACE_BASE_CONTENT, msg.arg1, 0);
mSplitPictureIsScheduled = false;
break;
@@ -1714,7 +1713,7 @@ public final class WebViewCore {
d.mNativeLayer, d.mNativeLayerRect);
}
WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true);
- mWebView.mPrivateHandler.obtainMessage(
+ mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.HIT_TEST_RESULT, hit)
.sendToTarget();
break;
@@ -1725,8 +1724,8 @@ public final class WebViewCore {
case AUTOFILL_FORM:
nativeAutoFillForm(mNativeClass, msg.arg1);
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.AUTOFILL_COMPLETE, null)
- .sendToTarget();
+ mWebViewClassic.mPrivateHandler.obtainMessage(
+ WebViewClassic.AUTOFILL_COMPLETE, null).sendToTarget();
break;
case EXECUTE_JS:
@@ -1734,7 +1733,8 @@ public final class WebViewCore {
if (DebugFlags.WEB_VIEW_CORE) {
Log.d(LOGTAG, "Executing JS : " + msg.obj);
}
- mBrowserFrame.stringByEvaluatingJavaScriptFromString((String) msg.obj);
+ mBrowserFrame.stringByEvaluatingJavaScriptFromString(
+ (String) msg.obj);
}
break;
case SCROLL_LAYER:
@@ -1756,7 +1756,8 @@ public final class WebViewCore {
handles[0], handles[1], handles[2],
handles[3]);
if (copiedText != null) {
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.COPY_TO_CLIPBOARD, copiedText)
+ mWebViewClassic.mPrivateHandler.obtainMessage(
+ WebViewClassic.COPY_TO_CLIPBOARD, copiedText)
.sendToTarget();
}
break;
@@ -1777,7 +1778,10 @@ public final class WebViewCore {
case SELECT_WORD_AT: {
int x = msg.arg1;
int y = msg.arg2;
- nativeSelectWordAt(mNativeClass, x, y);
+ if (!nativeSelectWordAt(mNativeClass, x, y)) {
+ mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE)
+ .sendToTarget();
+ }
break;
}
case SELECT_ALL:
@@ -2028,7 +2032,7 @@ public final class WebViewCore {
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
if (canTakeFocusDirection != 0 && isDown) {
- Message m = mWebView.mPrivateHandler.obtainMessage(
+ Message m = mWebViewClassic.mPrivateHandler.obtainMessage(
WebViewClassic.TAKE_FOCUS);
m.arg1 = canTakeFocusDirection;
m.sendToTarget();
@@ -2101,7 +2105,8 @@ public final class WebViewCore {
width = mViewportWidth;
} else {
// For mobile web site.
- width = Math.round(mWebView.getViewWidth() / mWebView.getDefaultZoomScale());
+ width = Math.round(mWebViewClassic.getViewWidth() /
+ mWebViewClassic.getDefaultZoomScale());
}
}
return width;
@@ -2193,8 +2198,8 @@ public final class WebViewCore {
// If anything more complex than position has been touched, let's do a full draw
webkitDraw();
}
- mWebView.mPrivateHandler.removeMessages(WebViewClassic.INVAL_RECT_MSG_ID);
- mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(mWebView.mPrivateHandler
+ mWebViewClassic.mPrivateHandler.removeMessages(WebViewClassic.INVAL_RECT_MSG_ID);
+ mWebViewClassic.mPrivateHandler.sendMessageAtFrontOfQueue(mWebViewClassic.mPrivateHandler
.obtainMessage(WebViewClassic.INVAL_RECT_MSG_ID));
}
@@ -2234,7 +2239,7 @@ public final class WebViewCore {
draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mInvalRegion,
draw.mContentSize);
if (draw.mBaseLayer == 0) {
- if (mWebView != null && !mWebView.isPaused()) {
+ if (mWebViewClassic != null && !mWebViewClassic.isPaused()) {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
mEventHub.sendMessageDelayed(Message.obtain(null, EventHub.WEBKIT_DRAW), 10);
} else {
@@ -2247,7 +2252,7 @@ public final class WebViewCore {
}
private void webkitDraw(DrawData draw) {
- if (mWebView != null) {
+ if (mWebViewClassic != null) {
draw.mFocusSizeChanged = nativeFocusBoundsChanged(mNativeClass);
draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
if (mSettings.getUseWideViewPort()) {
@@ -2266,7 +2271,8 @@ public final class WebViewCore {
mFirstLayoutForNonStandardLoad = false;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
- Message.obtain(mWebView.mPrivateHandler,
+ pauseWebKitDraw();
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
}
}
@@ -2356,7 +2362,7 @@ public final class WebViewCore {
// called from JNI or WebView thread
/* package */ void contentDraw() {
synchronized (this) {
- if (mWebView == null || mBrowserFrame == null) {
+ if (mWebViewClassic == null || mBrowserFrame == null) {
// We were destroyed
return;
}
@@ -2394,8 +2400,8 @@ public final class WebViewCore {
mRestoredY = y;
return;
}
- if (mWebView != null) {
- Message msg = Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message msg = Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0,
onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
if (mDrawIsScheduled) {
@@ -2417,8 +2423,8 @@ public final class WebViewCore {
in WebView since it (and its thread) know the current scale factor.
*/
private void sendViewInvalidate(int left, int top, int right, int bottom) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.INVAL_RECT_MSG_ID,
new Rect(left, top, right, bottom)).sendToTarget();
}
@@ -2433,10 +2439,20 @@ public final class WebViewCore {
mRepaintScheduled = false;
}
- // Gets the WebView corresponding to this WebViewCore. Note that the
- // WebView object must only be used on the UI thread.
- /* package */ WebViewClassic getWebView() {
- return mWebView;
+ // Gets the WebViewClassic corresponding to this WebViewCore. Note that the
+ // WebViewClassic object must only be used on the UI thread.
+ /* package */ WebViewClassic getWebViewClassic() {
+ return mWebViewClassic;
+ }
+
+ // Called by JNI
+ private WebView getWebView() {
+ return mWebViewClassic.getWebView();
+ }
+
+ // Called by JNI
+ private void sendPluginDrawMsg() {
+ sendMessage(EventHub.PLUGIN_SURFACE_READY);
}
private native void setViewportSettingsFromNative(int nativeClass);
@@ -2449,7 +2465,7 @@ public final class WebViewCore {
mBrowserFrame.didFirstLayout();
- if (mWebView == null) return;
+ if (mWebViewClassic == null) return;
boolean updateViewState = standardLoad || mIsRestored;
setupViewport(updateViewState);
@@ -2457,11 +2473,11 @@ public final class WebViewCore {
// be called after the WebView updates its state. If updateRestoreState
// is false, start to draw now as it is ready.
if (!updateViewState) {
- mWebView.mViewManager.postReadyToDrawAll();
+ mWebViewClassic.mViewManager.postReadyToDrawAll();
}
// remove the touch highlight when moving to a new page
- mWebView.mPrivateHandler.sendEmptyMessage(
+ mWebViewClassic.mPrivateHandler.sendEmptyMessage(
WebViewClassic.HIT_TEST_RESULT);
// reset the scroll position, the restored offset and scales
@@ -2477,7 +2493,7 @@ public final class WebViewCore {
}
private void setupViewport(boolean updateViewState) {
- if (mWebView == null || mSettings == null) {
+ if (mWebViewClassic == null || mSettings == null) {
// We've been destroyed or are being destroyed, return early
return;
}
@@ -2525,8 +2541,8 @@ public final class WebViewCore {
adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
/ mViewportDensityDpi;
}
- if (adjust != mWebView.getDefaultZoomScale()) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (adjust != mWebViewClassic.getDefaultZoomScale()) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
}
int defaultScale = (int) (adjust * 100);
@@ -2577,7 +2593,7 @@ public final class WebViewCore {
// for non-mobile site, we don't need minPrefWidth, set it as 0
viewState.mScrollX = 0;
viewState.mShouldStartScrolledRight = false;
- Message.obtain(mWebView.mPrivateHandler,
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
return;
}
@@ -2591,7 +2607,7 @@ public final class WebViewCore {
// this may happen when WebView just starts. This is not perfect as
// we call WebView method from WebCore thread. But not perfect
// reference is better than no reference.
- webViewWidth = mWebView.getViewWidth();
+ webViewWidth = mWebViewClassic.getViewWidth();
viewportWidth = (int) (webViewWidth / adjust);
if (viewportWidth == 0) {
if (DebugFlags.WEB_VIEW_CORE) {
@@ -2640,17 +2656,17 @@ public final class WebViewCore {
}
}
- if (mWebView.mHeightCanMeasure) {
+ if (mWebViewClassic.mHeightCanMeasure) {
// Trick to ensure that the Picture has the exact height for the
// content by forcing to layout with 0 height after the page is
// ready, which is indicated by didFirstLayout. This is essential to
// get rid of the white space in the GMail which uses WebView for
// message view.
- mWebView.mLastHeightSent = 0;
+ mWebViewClassic.mLastHeightSent = 0;
// Send a negative scale to indicate that WebCore should reuse
// the current scale
WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
- data.mWidth = mWebView.mLastWidthSent;
+ data.mWidth = mWebViewClassic.mLastWidthSent;
data.mHeight = 0;
// if mHeightCanMeasure is true, getUseWideViewPort() can't be
// true. It is safe to use mWidth for mTextWrapWidth.
@@ -2671,7 +2687,7 @@ public final class WebViewCore {
if (viewportWidth == 0) {
// Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
// to WebViewCore
- mWebView.mLastWidthSent = 0;
+ mWebViewClassic.mLastWidthSent = 0;
} else {
WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
// mViewScale as 0 means it is in zoom overview mode. So we don't
@@ -2699,7 +2715,7 @@ public final class WebViewCore {
if (mSettings.isNarrowColumnLayout()) {
// In case of automatic text reflow in fixed view port mode.
mInitialViewState.mTextWrapScale =
- mWebView.computeReadingLevelScale(data.mScale);
+ mWebViewClassic.computeReadingLevelScale(data.mScale);
}
} else {
// Scale is given such as when page is restored, use it.
@@ -2720,7 +2736,7 @@ public final class WebViewCore {
// are calling a WebView method from the WebCore thread. But this is preferable
// to syncing an incorrect height.
data.mHeight = mCurrentViewHeight == 0 ?
- Math.round(mWebView.getViewHeight() / data.mScale)
+ Math.round(mWebViewClassic.getViewHeight() / data.mScale)
: Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth);
data.mTextWrapWidth = Math.round(webViewWidth
/ mInitialViewState.mTextWrapScale);
@@ -2749,8 +2765,8 @@ public final class WebViewCore {
// called by JNI
private void needTouchEvents(boolean need) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
.sendToTarget();
}
@@ -2759,8 +2775,8 @@ public final class WebViewCore {
// called by JNI
private void updateTextfield(int ptr, boolean changeToPassword,
String text, int textGeneration) {
- if (mWebView != null) {
- Message msg = Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message msg = Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
textGeneration, text);
msg.getData().putBoolean("password", changeToPassword);
@@ -2771,8 +2787,8 @@ public final class WebViewCore {
// called by JNI
private void updateTextSelection(int pointer, int start, int end,
int textGeneration, int selectionPtr) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
new TextSelectionData(start, end, selectionPtr)).sendToTarget();
}
@@ -2781,10 +2797,10 @@ public final class WebViewCore {
// called by JNI
private void updateTextSizeAndScroll(int pointer, int width, int height,
int scrollX, int scrollY) {
- if (mWebView != null) {
+ if (mWebViewClassic != null) {
Rect rect = new Rect(-scrollX, -scrollY, width - scrollX,
height - scrollY);
- Message.obtain(mWebView.mPrivateHandler,
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.EDIT_TEXT_SIZE_CHANGED, pointer, 0, rect)
.sendToTarget();
}
@@ -2792,20 +2808,20 @@ public final class WebViewCore {
// called by JNI
private void clearTextEntry() {
- if (mWebView == null) return;
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic == null) return;
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget();
}
// called by JNI
private void initEditField(int start, int end, int selectionPtr,
TextFieldInitData initData) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- Message.obtain(mWebView.mPrivateHandler,
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget();
- Message.obtain(mWebView.mPrivateHandler,
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID,
initData.mFieldPointer, 0,
new TextSelectionData(start, end, selectionPtr))
@@ -2815,10 +2831,10 @@ public final class WebViewCore {
// called by JNI
private void updateMatchCount(int matchIndex, int matchCount,
String findText) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- Message.obtain(mWebView.mPrivateHandler,
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.UPDATE_MATCH_COUNT, matchIndex, matchCount,
findText).sendToTarget();
}
@@ -2842,32 +2858,32 @@ public final class WebViewCore {
// called by JNI
private void requestListBox(String[] array, int[] enabledArray,
int[] selectedArray) {
- if (mWebView != null) {
- mWebView.requestListBox(array, enabledArray, selectedArray);
+ if (mWebViewClassic != null) {
+ mWebViewClassic.requestListBox(array, enabledArray, selectedArray);
}
}
// called by JNI
private void requestListBox(String[] array, int[] enabledArray,
int selection) {
- if (mWebView != null) {
- mWebView.requestListBox(array, enabledArray, selection);
+ if (mWebViewClassic != null) {
+ mWebViewClassic.requestListBox(array, enabledArray, selection);
}
}
// called by JNI
private void requestKeyboard(boolean showKeyboard) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
.sendToTarget();
}
}
private void setWebTextViewAutoFillable(int queryId, String preview) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE,
+ if (mWebViewClassic != null) {
+ Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE,
new AutoFillData(queryId, preview))
.sendToTarget();
}
@@ -2879,8 +2895,9 @@ public final class WebViewCore {
// called by JNI
private void keepScreenOn(boolean screenOn) {
- if (mWebView != null) {
- Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SCREEN_ON);
+ if (mWebViewClassic != null) {
+ Message message = mWebViewClassic.mPrivateHandler.obtainMessage(
+ WebViewClassic.SCREEN_ON);
message.arg1 = screenOn ? 1 : 0;
message.sendToTarget();
}
@@ -2889,7 +2906,7 @@ public final class WebViewCore {
// called by JNI
private Class<?> getPluginClass(String libName, String clsName) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return null;
}
@@ -2916,11 +2933,12 @@ public final class WebViewCore {
// called by JNI. PluginWidget function to launch a full-screen view using a
// View object provided by the plugin class.
private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_FULLSCREEN);
+ Message message = mWebViewClassic.mPrivateHandler.obtainMessage(
+ WebViewClassic.SHOW_FULLSCREEN);
message.obj = childView.mView;
message.arg1 = orientation;
message.arg2 = npp;
@@ -2929,15 +2947,15 @@ public final class WebViewCore {
// called by JNI
private void hideFullScreenPlugin() {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
+ mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
.sendToTarget();
}
private ViewManager.ChildView createSurface(View pluginView) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return null;
}
@@ -2952,7 +2970,7 @@ public final class WebViewCore {
if(pluginView instanceof SurfaceView)
((SurfaceView)pluginView).setZOrderOnTop(true);
- ViewManager.ChildView view = mWebView.mViewManager.createView();
+ ViewManager.ChildView view = mWebViewClassic.mViewManager.createView();
view.mView = pluginView;
return view;
}
@@ -2992,7 +3010,7 @@ public final class WebViewCore {
private void showRect(int left, int top, int width, int height,
int contentWidth, int contentHeight, float xPercentInDoc,
float xPercentInView, float yPercentInDoc, float yPercentInView) {
- if (mWebView != null) {
+ if (mWebViewClassic != null) {
ShowRectData data = new ShowRectData();
data.mLeft = left;
data.mTop = top;
@@ -3004,26 +3022,26 @@ public final class WebViewCore {
data.mXPercentInView = xPercentInView;
data.mYPercentInDoc = yPercentInDoc;
data.mYPercentInView = yPercentInView;
- Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID,
+ Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID,
data).sendToTarget();
}
}
// called by JNI
private void centerFitRect(int x, int y, int width, int height) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT,
+ mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT,
new Rect(x, y, x + width, y + height)).sendToTarget();
}
// called by JNI
private void setScrollbarModes(int hMode, int vMode) {
- if (mWebView == null) {
+ if (mWebViewClassic == null) {
return;
}
- mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES,
+ mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES,
hMode, vMode).sendToTarget();
}
@@ -3106,7 +3124,7 @@ public final class WebViewCore {
private native void nativeSelectText(int nativeClass,
int startX, int startY, int endX, int endY);
private native void nativeClearTextSelection(int nativeClass);
- private native void nativeSelectWordAt(int nativeClass, int x, int y);
+ private native boolean nativeSelectWordAt(int nativeClass, int x, int y);
private native void nativeSelectAll(int nativeClass);
private static native void nativeCertTrustChanged();
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 757a619..6c35f19 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -144,7 +144,6 @@ public class WebViewDatabase {
null);
}
}
- mDatabase.enableWriteAheadLogging();
// mDatabase should not be null,
// the only case is RequestAPI test has problem to create db
@@ -163,11 +162,6 @@ public class WebViewDatabase {
mDatabase.endTransaction();
}
}
-
- // use per table Mutex lock, turn off database lock, this
- // improves performance as database's ReentrantLock is
- // expansive
- mDatabase.setLockingEnabled(false);
}
private static void upgradeDatabase() {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 9016fbc..f049198 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -19,6 +19,7 @@ package android.webkit;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -191,7 +192,7 @@ public interface WebViewProvider {
public WebBackForwardList copyBackForwardList();
- public void setFindListener(FindListener listener);
+ public void setFindListener(WebView.FindListener listener);
public void findNext(boolean forward);
@@ -337,6 +338,8 @@ public interface WebViewProvider {
public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate);
public void setBackgroundColor(int color);
+
+ public void setLayerType(int layerType, Paint paint);
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index e36afa3..ca5648a 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -133,6 +133,16 @@ public abstract class AbsSeekBar extends ProgressBar {
}
/**
+ * Return the drawable used to represent the scroll thumb - the component that
+ * the user can drag back and forth indicating the current value by its position.
+ *
+ * @return The current thumb drawable
+ */
+ public Drawable getThumb() {
+ return mThumb;
+ }
+
+ /**
* @see #setThumbOffset(int)
*/
public int getThumbOffset() {
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 5096227..aea029b 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -127,13 +127,29 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
}
/**
- * How long to wait before flipping to the next view
+ * Returns the flip interval, in milliseconds.
*
- * @param milliseconds
- * time in milliseconds
+ * @return the flip interval in milliseconds
+ *
+ * @see #setFlipInterval(int)
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
+ */
+ public int getFlipInterval() {
+ return mFlipInterval;
+ }
+
+ /**
+ * How long to wait before flipping to the next view.
+ *
+ * @param flipInterval flip interval in milliseconds
+ *
+ * @see #getFlipInterval()
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
*/
- public void setFlipInterval(int milliseconds) {
- mFlipInterval = milliseconds;
+ public void setFlipInterval(int flipInterval) {
+ mFlipInterval = flipInterval;
}
/**
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index fd93980..c5066b6 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -29,8 +29,8 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@@ -280,9 +280,7 @@ public class DatePicker extends FrameLayout {
reorderSpinners();
// set content descriptions
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- setContentDescriptions();
- }
+ setContentDescriptions();
}
/**
@@ -717,20 +715,27 @@ public class DatePicker extends FrameLayout {
private void setContentDescriptions() {
// Day
- String text = mContext.getString(R.string.date_picker_increment_day_button);
- mDaySpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.date_picker_decrement_day_button);
- mDaySpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mDaySpinner, R.id.increment,
+ R.string.date_picker_increment_day_button);
+ trySetContentDescription(mDaySpinner, R.id.decrement,
+ R.string.date_picker_decrement_day_button);
// Month
- text = mContext.getString(R.string.date_picker_increment_month_button);
- mMonthSpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.date_picker_decrement_month_button);
- mMonthSpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mMonthSpinner, R.id.increment,
+ R.string.date_picker_increment_month_button);
+ trySetContentDescription(mMonthSpinner, R.id.decrement,
+ R.string.date_picker_decrement_month_button);
// Year
- text = mContext.getString(R.string.date_picker_increment_year_button);
- mYearSpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.date_picker_decrement_year_button);
- mYearSpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mYearSpinner, R.id.increment,
+ R.string.date_picker_increment_year_button);
+ trySetContentDescription(mYearSpinner, R.id.decrement,
+ R.string.date_picker_decrement_year_button);
+ }
+
+ private void trySetContentDescription(View root, int viewId, int contDescResId) {
+ View target = root.findViewById(viewId);
+ if (target != null) {
+ target.setContentDescription(mContext.getString(contDescResId));
+ }
}
private void updateInputState() {
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 739bcce..0f1dab5 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1908,7 +1908,8 @@ public class GridView extends AbsListView {
}
/**
- * Describes how the child views are horizontally aligned. Defaults to Gravity.LEFT
+ * Set the gravity for this grid. Gravity describes how the child views
+ * are horizontally aligned. Defaults to Gravity.LEFT
*
* @param gravity the gravity to apply to this grid's children
*
@@ -1922,6 +1923,17 @@ public class GridView extends AbsListView {
}
/**
+ * Describes how the child views are horizontally aligned. Defaults to Gravity.LEFT
+ *
+ * @return the gravity that will be applied to this grid's children
+ *
+ * @attr ref android.R.styleable#GridView_gravity
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
+ /**
* Set the amount of horizontal (x) spacing to place between each item
* in the grid.
*
@@ -1937,6 +1949,44 @@ public class GridView extends AbsListView {
}
}
+ /**
+ * Returns the amount of horizontal spacing currently used between each item in the grid.
+ *
+ * <p>This is only accurate for the current layout. If {@link #setHorizontalSpacing(int)}
+ * has been called but layout is not yet complete, this method may return a stale value.
+ * To get the horizontal spacing that was explicitly requested use
+ * {@link #getRequestedHorizontalSpacing()}.</p>
+ *
+ * @return Current horizontal spacing between each item in pixels
+ *
+ * @see #setHorizontalSpacing(int)
+ * @see #getRequestedHorizontalSpacing()
+ *
+ * @attr ref android.R.styleable#GridView_horizontalSpacing
+ */
+ public int getHorizontalSpacing() {
+ return mHorizontalSpacing;
+ }
+
+ /**
+ * Returns the requested amount of horizontal spacing between each item in the grid.
+ *
+ * <p>The value returned may have been supplied during inflation as part of a style,
+ * the default GridView style, or by a call to {@link #setHorizontalSpacing(int)}.
+ * If layout is not yet complete or if GridView calculated a different horizontal spacing
+ * from what was requested, this may return a different value from
+ * {@link #getHorizontalSpacing()}.</p>
+ *
+ * @return The currently requested horizontal spacing between items, in pixels
+ *
+ * @see #setHorizontalSpacing(int)
+ * @see #getHorizontalSpacing()
+ *
+ * @attr ref android.R.styleable#GridView_horizontalSpacing
+ */
+ public int getRequestedHorizontalSpacing() {
+ return mRequestedHorizontalSpacing;
+ }
/**
* Set the amount of vertical (y) spacing to place between each item
@@ -1945,6 +1995,8 @@ public class GridView extends AbsListView {
* @param verticalSpacing The amount of vertical space between items,
* in pixels.
*
+ * @see #getVerticalSpacing()
+ *
* @attr ref android.R.styleable#GridView_verticalSpacing
*/
public void setVerticalSpacing(int verticalSpacing) {
@@ -1955,6 +2007,19 @@ public class GridView extends AbsListView {
}
/**
+ * Returns the amount of vertical spacing between each item in the grid.
+ *
+ * @return The vertical spacing between items in pixels
+ *
+ * @see #setVerticalSpacing(int)
+ *
+ * @attr ref android.R.styleable#GridView_verticalSpacing
+ */
+ public int getVerticalSpacing() {
+ return mVerticalSpacing;
+ }
+
+ /**
* Control how items are stretched to fill their space.
*
* @param stretchMode Either {@link #NO_STRETCH},
@@ -1988,6 +2053,39 @@ public class GridView extends AbsListView {
}
/**
+ * Return the width of a column in the grid.
+ *
+ * <p>This may not be valid yet if a layout is pending.</p>
+ *
+ * @return The column width in pixels
+ *
+ * @see #setColumnWidth(int)
+ * @see #getRequestedColumnWidth()
+ *
+ * @attr ref android.R.styleable#GridView_columnWidth
+ */
+ public int getColumnWidth() {
+ return mColumnWidth;
+ }
+
+ /**
+ * Return the requested width of a column in the grid.
+ *
+ * <p>This may not be the actual column width used. Use {@link #getColumnWidth()}
+ * to retrieve the current real width of a column.</p>
+ *
+ * @return The requested column width in pixels
+ *
+ * @see #setColumnWidth(int)
+ * @see #getColumnWidth()
+ *
+ * @attr ref android.R.styleable#GridView_columnWidth
+ */
+ public int getRequestedColumnWidth() {
+ return mRequestedColumnWidth;
+ }
+
+ /**
* Set the number of columns in the grid
*
* @param numColumns The desired number of columns.
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 0db6ef2..4e13ea1 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -715,6 +715,7 @@ public class HorizontalScrollView extends FrameLayout {
} else {
super.scrollTo(scrollX, scrollY);
}
+
awakenScrollBars();
}
@@ -1204,10 +1205,9 @@ public class HorizontalScrollView extends FrameLayout {
}
}
- awakenScrollBars();
-
- // Keep on drawing until the animation has finished.
- postInvalidate();
+ if (!awakenScrollBars()) {
+ invalidate();
+ }
}
}
@@ -1414,7 +1414,7 @@ public class HorizontalScrollView extends FrameLayout {
/**
* Return true if child is a descendant of parent, (or equal to the parent).
*/
- private boolean isViewDescendantOf(View child, View parent) {
+ private static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
@@ -1524,7 +1524,7 @@ public class HorizontalScrollView extends FrameLayout {
}
}
- private int clamp(int n, int my, int child) {
+ private static int clamp(int n, int my, int child) {
if (my >= child || n < 0) {
return 0;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 3001ea1..b1a75e1 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -223,11 +223,28 @@ public class ImageView extends View {
}
/**
+ * True when ImageView is adjusting its bounds
+ * to preserve the aspect ratio of its drawable
+ *
+ * @return whether to adjust the bounds of this view
+ * to presrve the original aspect ratio of the drawable
+ *
+ * @see #setAdjustViewBounds(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_adjustViewBounds
+ */
+ public boolean getAdjustViewBounds() {
+ return mAdjustViewBounds;
+ }
+
+ /**
* Set this to true if you want the ImageView to adjust its bounds
* to preserve the aspect ratio of its drawable.
* @param adjustViewBounds Whether to adjust the bounds of this view
* to presrve the original aspect ratio of the drawable
*
+ * @see #getAdjustViewBounds()
+ *
* @attr ref android.R.styleable#ImageView_adjustViewBounds
*/
@android.view.RemotableViewMethod
@@ -237,7 +254,20 @@ public class ImageView extends View {
setScaleType(ScaleType.FIT_CENTER);
}
}
-
+
+ /**
+ * The maximum width of this view.
+ *
+ * @return The maximum width of this view
+ *
+ * @see #setMaxWidth(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxWidth
+ */
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
/**
* An optional argument to supply a maximum width for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum
@@ -253,14 +283,29 @@ public class ImageView extends View {
* </p>
*
* @param maxWidth maximum width for this view
- *
+ *
+ * @see #getMaxWidth()
+ *
* @attr ref android.R.styleable#ImageView_maxWidth
*/
@android.view.RemotableViewMethod
public void setMaxWidth(int maxWidth) {
mMaxWidth = maxWidth;
}
-
+
+ /**
+ * The maximum height of this view.
+ *
+ * @return The maximum height of this view
+ *
+ * @see #setMaxHeight(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxHeight
+ */
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
/**
* An optional argument to supply a maximum height for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
@@ -276,7 +321,9 @@ public class ImageView extends View {
* </p>
*
* @param maxHeight maximum height for this view
- *
+ *
+ * @see #getMaxHeight()
+ *
* @attr ref android.R.styleable#ImageView_maxHeight
*/
@android.view.RemotableViewMethod
@@ -522,7 +569,37 @@ public class ImageView extends View {
invalidate();
}
}
-
+
+ /**
+ * Return whether this ImageView crops to padding.
+ *
+ * @return whether this ImageView crops to padding
+ *
+ * @see #setCropToPadding(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public boolean getCropToPadding() {
+ return mCropToPadding;
+ }
+
+ /**
+ * Sets whether this ImageView will crop to padding.
+ *
+ * @param cropToPadding whether this ImageView will crop to padding
+ *
+ * @see #getCropToPadding()
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public void setCropToPadding(boolean cropToPadding) {
+ if (mCropToPadding != cropToPadding) {
+ mCropToPadding = cropToPadding;
+ requestLayout();
+ invalidate();
+ }
+ }
+
private void resolveUri() {
if (mDrawable != null) {
return;
@@ -997,11 +1074,24 @@ public class ImageView extends View {
public final void clearColorFilter() {
setColorFilter(null);
}
-
+
+ /**
+ * Returns the active color filter for this ImageView.
+ *
+ * @return the active color filter for this ImageView
+ *
+ * @see #setColorFilter(android.graphics.ColorFilter)
+ */
+ public ColorFilter getColorFilter() {
+ return mColorFilter;
+ }
+
/**
* Apply an arbitrary colorfilter to the image.
*
* @param cf the colorfilter to apply (may be null)
+ *
+ * @see #getColorFilter()
*/
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
@@ -1012,6 +1102,37 @@ public class ImageView extends View {
}
}
+ /**
+ * Returns the alpha that will be applied to the drawable of this ImageView.
+ *
+ * @return the alpha that will be applied to the drawable of this ImageView
+ *
+ * @see #setImageAlpha(int)
+ */
+ public int getImageAlpha() {
+ return mAlpha;
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @see #getImageAlpha()
+ */
+ @RemotableViewMethod
+ public void setImageAlpha(int alpha) {
+ setAlpha(alpha);
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @deprecated use #setImageAlpha(int) instead
+ */
+ @Deprecated
@RemotableViewMethod
public void setAlpha(int alpha) {
alpha &= 0xFF; // keep it legal
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 3335da0..4e56cd6 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -16,10 +16,6 @@
package android.widget;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -48,22 +44,41 @@ import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A widget that enables the user to select a number form a predefined range.
- * The widget presents an input field and up and down buttons for selecting the
- * current value. Pressing/long-pressing the up and down buttons increments and
- * decrements the current value respectively. Touching the input field shows a
- * scroll wheel, which when touched allows direct edit
- * of the current value. Sliding gestures up or down hide the buttons and the
- * input filed, show and rotate the scroll wheel. Flinging is
- * also supported. The widget enables mapping from positions to strings such
- * that, instead of the position index, the corresponding string is displayed.
+ * There are two flavors of this widget and which one is presented to the user
+ * depends on the current theme.
+ * <ul>
+ * <li>
+ * If the current theme is derived from {@link android.R.style#Theme} the widget
+ * presents the current value as an editable input field with an increment button
+ * above and a decrement button below. Long pressing the buttons allows for a quick
+ * change of the current value. Tapping on the input field allows to type in
+ * a desired value.
+ * </li>
+ * <li>
+ * If the current theme is derived from {@link android.R.style#Theme_Holo} or
+ * {@link android.R.style#Theme_Holo_Light} the widget presents the current
+ * value as an editable input field with a lesser value above and a greater
+ * value below. Tapping on the lesser or greater value selects it by animating
+ * the number axis up or down to make the chosen value current. Flinging up
+ * or down allows for multiple increments or decrements of the current value.
+ * Long pressing on the lesser and greater values also allows for a quick change
+ * of the current value. Tapping on the current value allows to type in a
+ * desired value.
+ * </li>
+ * </ul>
* <p>
* For an example of using this widget, see {@link android.widget.TimePicker}.
* </p>
@@ -74,7 +89,7 @@ public class NumberPicker extends LinearLayout {
/**
* The number of items show in the selector wheel.
*/
- public static final int SELECTOR_WHEEL_ITEM_COUNT = 5;
+ private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;
/**
* The default update interval during long press.
@@ -84,7 +99,7 @@ public class NumberPicker extends LinearLayout {
/**
* The index of the middle selector item.
*/
- private static final int SELECTOR_MIDDLE_ITEM_INDEX = 2;
+ private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2;
/**
* The coefficient by which to adjust (divide) the max fling velocity.
@@ -97,19 +112,12 @@ public class NumberPicker extends LinearLayout {
private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
/**
- * The duration of scrolling to the next/previous value while changing
- * the current value by one, i.e. increment or decrement.
+ * The duration of scrolling to the next/previous value while changing the
+ * current value by one, i.e. increment or decrement.
*/
private static final int CHANGE_CURRENT_BY_ONE_SCROLL_DURATION = 300;
/**
- * The the delay for showing the input controls after a single tap on the
- * input text.
- */
- private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration
- .getDoubleTapTimeout();
-
- /**
* The strength of fading in the top and bottom while drawing the selector.
*/
private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
@@ -120,56 +128,31 @@ public class NumberPicker extends LinearLayout {
private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
/**
- * In this state the selector wheel is not shown.
- */
- private static final int SELECTOR_WHEEL_STATE_NONE = 0;
-
- /**
- * In this state the selector wheel is small.
- */
- private static final int SELECTOR_WHEEL_STATE_SMALL = 1;
-
- /**
- * In this state the selector wheel is large.
- */
- private static final int SELECTOR_WHEEL_STATE_LARGE = 2;
-
- /**
- * The alpha of the selector wheel when it is bright.
- */
- private static final int SELECTOR_WHEEL_BRIGHT_ALPHA = 255;
-
- /**
- * The alpha of the selector wheel when it is dimmed.
+ * The default unscaled distance between the selection dividers.
*/
- private static final int SELECTOR_WHEEL_DIM_ALPHA = 60;
+ private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
/**
- * The alpha for the increment/decrement button when it is transparent.
+ * The default unscaled minimal distance for a swipe to be considered a fling.
*/
- private static final int BUTTON_ALPHA_TRANSPARENT = 0;
+ private static final int UNSCALED_DEFAULT_MIN_FLING_DISTANCE = 150;
/**
- * The alpha for the increment/decrement button when it is opaque.
+ * Coefficient for adjusting touch scroll distance.
*/
- private static final int BUTTON_ALPHA_OPAQUE = 1;
+ private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.5f;
/**
- * The property for setting the selector paint.
+ * The resource id for the default layout.
*/
- private static final String PROPERTY_SELECTOR_PAINT_ALPHA = "selectorPaintAlpha";
-
- /**
- * The property for setting the increment/decrement button alpha.
- */
- private static final String PROPERTY_BUTTON_ALPHA = "alpha";
+ private static final int DEFAULT_LAYOUT_RESOURCE_ID = R.layout.number_picker;
/**
* The numbers accepted by the input text's {@link Filter}
*/
private static final char[] DIGIT_CHARACTERS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
- };
+ };
/**
* Constant for unspecified size.
@@ -215,6 +198,11 @@ public class NumberPicker extends LinearLayout {
private final EditText mInputText;
/**
+ * The distance between the two selection dividers.
+ */
+ private final int mSelectionDividersDistance;
+
+ /**
* The min height of this widget.
*/
private final int mMinHeight;
@@ -245,6 +233,11 @@ public class NumberPicker extends LinearLayout {
private final int mTextSize;
/**
+ * The minimal distance for a swipe to be considered a fling.
+ */
+ private final int mMinFlingDistance;
+
+ /**
* The height of the gap between text elements if the selector wheel.
*/
private int mSelectorTextGapHeight;
@@ -297,10 +290,7 @@ public class NumberPicker extends LinearLayout {
/**
* The selector indices whose value are show by the selector.
*/
- private final int[] mSelectorIndices = new int[] {
- Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE,
- Integer.MIN_VALUE
- };
+ private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];
/**
* The {@link Paint} for drawing the selector.
@@ -343,25 +333,15 @@ public class NumberPicker extends LinearLayout {
private SetSelectionCommand mSetSelectionCommand;
/**
- * Handle to the reusable command for adjusting the scroller.
- */
- private AdjustScrollerCommand mAdjustScrollerCommand;
-
- /**
* Handle to the reusable command for changing the current value from long
* press by one.
*/
private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
/**
- * {@link Animator} for showing the up/down arrows.
- */
- private final AnimatorSet mShowInputControlsAnimator;
-
- /**
- * {@link Animator} for dimming the selector wheel.
+ * Command for beginning an edit of the current value via IME on long press.
*/
- private final Animator mDimSelectorWheelAnimator;
+ private BeginSoftInputOnLongPressCommand mBeginSoftInputOnLongPressCommand;
/**
* The Y position of the last down event.
@@ -369,24 +349,14 @@ public class NumberPicker extends LinearLayout {
private float mLastDownEventY;
/**
- * The Y position of the last motion event.
+ * The time of the last down event.
*/
- private float mLastMotionEventY;
+ private long mLastDownEventTime;
/**
- * Flag if to check for double tap and potentially start edit.
+ * The Y position of the last down or move event.
*/
- private boolean mCheckBeginEditOnUpEvent;
-
- /**
- * Flag if to adjust the selector wheel on next up event.
- */
- private boolean mAdjustScrollerOnUpEvent;
-
- /**
- * The state of the selector wheel.
- */
- private int mSelectorWheelState;
+ private float mLastDownOrMoveEventY;
/**
* Determines speed during touch scrolling.
@@ -419,9 +389,9 @@ public class NumberPicker extends LinearLayout {
private final int mSolidColor;
/**
- * Flag indicating if this widget supports flinging.
+ * Flag whether this widget has a selector wheel.
*/
- private final boolean mFlingable;
+ private final boolean mHasSelectorWheel;
/**
* Divider for showing item to be selected while scrolling
@@ -434,29 +404,40 @@ public class NumberPicker extends LinearLayout {
private final int mSelectionDividerHeight;
/**
- * Reusable {@link Rect} instance.
+ * The current scroll state of the number picker.
*/
- private final Rect mTempRect = new Rect();
+ private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
/**
- * The current scroll state of the number picker.
+ * Flag whether to ignore move events - we ignore such when we show in IME
+ * to prevent the content from scrolling.
*/
- private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+ private boolean mIngonreMoveEvents;
/**
- * The duration of the animation for showing the input controls.
+ * Flag whether to show soft input on tap.
*/
- private final long mShowInputControlsAnimimationDuration;
+ private boolean mShowSoftInputOnTap;
/**
- * Flag whether the scoll wheel and the fading edges have been initialized.
+ * The top of the top selection divider.
*/
- private boolean mScrollWheelAndFadingEdgesInitialized;
+ private int mTopSelectionDividerTop;
/**
- * The time of the last up event.
+ * The bottom of the bottom selection divider.
*/
- private long mLastUpEventTimeMillis;
+ private int mBottomSelectionDividerBottom;
+
+ /**
+ * The virtual id of the last hovered child.
+ */
+ private int mLastHoveredChildVirtualViewId;
+
+ /**
+ * Provider to report to clients the semantic structure of this widget.
+ */
+ private AccessibilityNodeProviderImpl mAccessibilityNodeProvider;
/**
* Interface to listen for changes of the current value.
@@ -484,7 +465,7 @@ public class NumberPicker extends LinearLayout {
public static int SCROLL_STATE_IDLE = 0;
/**
- * The user is scrolling using touch, and their finger is still on the screen.
+ * The user is scrolling using touch, and his finger is still on the screen.
*/
public static int SCROLL_STATE_TOUCH_SCROLL = 1;
@@ -549,58 +530,78 @@ public class NumberPicker extends LinearLayout {
super(context, attrs, defStyle);
// process style attributes
- TypedArray attributesArray = context.obtainStyledAttributes(attrs,
- R.styleable.NumberPicker, defStyle, 0);
+ TypedArray attributesArray = context.obtainStyledAttributes(
+ attrs, R.styleable.NumberPicker, defStyle, 0);
+ final int layoutResId = attributesArray.getResourceId(
+ R.styleable.NumberPicker_internalLayout, DEFAULT_LAYOUT_RESOURCE_ID);
+
+ mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
+
mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
- mFlingable = attributesArray.getBoolean(R.styleable.NumberPicker_flingable, true);
+
mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
- int defSelectionDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
+
+ final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
getResources().getDisplayMetrics());
mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
+
+ final int defSelectionDividerDistance = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE,
+ getResources().getDisplayMetrics());
+ mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
+
+ final int defMinFlingDistance = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_MIN_FLING_DISTANCE,
+ getResources().getDisplayMetrics());
+ mMinFlingDistance = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_minFlingDistance, defMinFlingDistance);
+
mMinHeight = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
+
mMaxHeight = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
&& mMinHeight > mMaxHeight) {
throw new IllegalArgumentException("minHeight > maxHeight");
}
- mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_internalMinWidth,
- SIZE_UNSPECIFIED);
- mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_internalMaxWidth,
- SIZE_UNSPECIFIED);
+
+ mMinWidth = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMinWidth, SIZE_UNSPECIFIED);
+
+ mMaxWidth = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMaxWidth, SIZE_UNSPECIFIED);
if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
&& mMinWidth > mMaxWidth) {
throw new IllegalArgumentException("minWidth > maxWidth");
}
+
mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE);
- attributesArray.recycle();
- mShowInputControlsAnimimationDuration = getResources().getInteger(
- R.integer.config_longAnimTime);
+ attributesArray.recycle();
// By default Linearlayout that we extend is not drawn. This is
// its draw() method is not called but dispatchDraw() is called
// directly (see ViewGroup.drawChild()). However, this class uses
// the fading edge effect implemented by View and we need our
// draw() method to be called. Therefore, we declare we will draw.
- setWillNotDraw(false);
- setSelectorWheelState(SELECTOR_WHEEL_STATE_NONE);
+ setWillNotDraw(!mHasSelectorWheel);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.number_picker, this, true);
+ inflater.inflate(layoutResId, this, true);
OnClickListener onClickListener = new OnClickListener() {
public void onClick(View v) {
hideSoftInput();
mInputText.clearFocus();
if (v.getId() == R.id.increment) {
- changeCurrentByOne(true);
+ changeValueByOne(true);
} else {
- changeCurrentByOne(false);
+ changeValueByOne(false);
}
}
};
@@ -610,23 +611,31 @@ public class NumberPicker extends LinearLayout {
hideSoftInput();
mInputText.clearFocus();
if (v.getId() == R.id.increment) {
- postChangeCurrentByOneFromLongPress(true);
+ postChangeCurrentByOneFromLongPress(true, 0);
} else {
- postChangeCurrentByOneFromLongPress(false);
+ postChangeCurrentByOneFromLongPress(false, 0);
}
return true;
}
};
// increment button
- mIncrementButton = (ImageButton) findViewById(R.id.increment);
- mIncrementButton.setOnClickListener(onClickListener);
- mIncrementButton.setOnLongClickListener(onLongClickListener);
+ if (!mHasSelectorWheel) {
+ mIncrementButton = (ImageButton) findViewById(R.id.increment);
+ mIncrementButton.setOnClickListener(onClickListener);
+ mIncrementButton.setOnLongClickListener(onLongClickListener);
+ } else {
+ mIncrementButton = null;
+ }
// decrement button
- mDecrementButton = (ImageButton) findViewById(R.id.decrement);
- mDecrementButton.setOnClickListener(onClickListener);
- mDecrementButton.setOnLongClickListener(onLongClickListener);
+ if (!mHasSelectorWheel) {
+ mDecrementButton = (ImageButton) findViewById(R.id.decrement);
+ mDecrementButton.setOnClickListener(onClickListener);
+ mDecrementButton.setOnLongClickListener(onLongClickListener);
+ } else {
+ mDecrementButton = null;
+ }
// input text
mInputText = (EditText) findViewById(R.id.numberpicker_input);
@@ -648,7 +657,6 @@ public class NumberPicker extends LinearLayout {
mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
// initialize constants
- mTouchSlop = ViewConfiguration.getTapTimeout();
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
@@ -667,69 +675,22 @@ public class NumberPicker extends LinearLayout {
paint.setColor(color);
mSelectorWheelPaint = paint;
- // create the animator for showing the input controls
- mDimSelectorWheelAnimator = ObjectAnimator.ofInt(this, PROPERTY_SELECTOR_PAINT_ALPHA,
- SELECTOR_WHEEL_BRIGHT_ALPHA, SELECTOR_WHEEL_DIM_ALPHA);
- final ObjectAnimator showIncrementButton = ObjectAnimator.ofFloat(mIncrementButton,
- PROPERTY_BUTTON_ALPHA, BUTTON_ALPHA_TRANSPARENT, BUTTON_ALPHA_OPAQUE);
- final ObjectAnimator showDecrementButton = ObjectAnimator.ofFloat(mDecrementButton,
- PROPERTY_BUTTON_ALPHA, BUTTON_ALPHA_TRANSPARENT, BUTTON_ALPHA_OPAQUE);
- mShowInputControlsAnimator = new AnimatorSet();
- mShowInputControlsAnimator.playTogether(mDimSelectorWheelAnimator, showIncrementButton,
- showDecrementButton);
- mShowInputControlsAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCanceled = false;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCanceled) {
- // if canceled => we still want the wheel drawn
- setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
- }
- mCanceled = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mShowInputControlsAnimator.isRunning()) {
- mCanceled = true;
- }
- }
- });
-
// create the fling and adjust scrollers
mFlingScroller = new Scroller(getContext(), null, true);
mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
updateInputTextView();
- updateIncrementAndDecrementButtonsVisibilityState();
-
- if (mFlingable) {
- if (isInEditMode()) {
- setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
- } else {
- // Start with shown selector wheel and hidden controls. When made
- // visible hide the selector and fade-in the controls to suggest
- // fling interaction.
- setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
- hideInputControls();
- }
- }
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mHasSelectorWheel) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
final int msrdWdth = getMeasuredWidth();
final int msrdHght = getMeasuredHeight();
- // Increment button at the top.
- final int inctBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
- final int incrBtnLeft = (msrdWdth - inctBtnMsrdWdth) / 2;
- final int incrBtnTop = 0;
- final int incrBtnRight = incrBtnLeft + inctBtnMsrdWdth;
- final int incrBtnBottom = incrBtnTop + mIncrementButton.getMeasuredHeight();
- mIncrementButton.layout(incrBtnLeft, incrBtnTop, incrBtnRight, incrBtnBottom);
-
// Input text centered horizontally.
final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
@@ -739,24 +700,23 @@ public class NumberPicker extends LinearLayout {
final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
- // Decrement button at the top.
- final int decrBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
- final int decrBtnLeft = (msrdWdth - decrBtnMsrdWdth) / 2;
- final int decrBtnTop = msrdHght - mDecrementButton.getMeasuredHeight();
- final int decrBtnRight = decrBtnLeft + decrBtnMsrdWdth;
- final int decrBtnBottom = msrdHght;
- mDecrementButton.layout(decrBtnLeft, decrBtnTop, decrBtnRight, decrBtnBottom);
-
- if (!mScrollWheelAndFadingEdgesInitialized) {
- mScrollWheelAndFadingEdgesInitialized = true;
+ if (changed) {
// need to do all this when we know our size
initializeSelectorWheel();
initializeFadingEdges();
+ mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
+ - mSelectionDividerHeight;
+ mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
+ + mSelectionDividersDistance;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!mHasSelectorWheel) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
// Try greedily to fit the max width and height.
final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
@@ -769,120 +729,143 @@ public class NumberPicker extends LinearLayout {
setMeasuredDimension(widthSize, heightSize);
}
+ /**
+ * Move to the final position of a scroller. Ensures to force finish the scroller
+ * and if it is not at its final position a scroll of the selector wheel is
+ * performed to fast forward to the final position.
+ *
+ * @param scroller The scroller to whose final position to get.
+ * @return True of the a move was performed, i.e. the scroller was not in final position.
+ */
+ private boolean moveToFinalScrollerPosition(Scroller scroller) {
+ scroller.forceFinished(true);
+ int amountToScroll = scroller.getFinalY() - scroller.getCurrY();
+ int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight;
+ int overshootAdjustment = mInitialScrollOffset - futureScrollOffset;
+ if (overshootAdjustment != 0) {
+ if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) {
+ if (overshootAdjustment > 0) {
+ overshootAdjustment -= mSelectorElementHeight;
+ } else {
+ overshootAdjustment += mSelectorElementHeight;
+ }
+ }
+ amountToScroll += overshootAdjustment;
+ scrollBy(0, amountToScroll);
+ return true;
+ }
+ return false;
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (!isEnabled() || !mFlingable) {
+ if (!mHasSelectorWheel || !isEnabled()) {
return false;
}
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionEventY = mLastDownEventY = event.getY();
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
removeAllCallbacks();
- mShowInputControlsAnimator.cancel();
- mDimSelectorWheelAnimator.cancel();
- mCheckBeginEditOnUpEvent = false;
- mAdjustScrollerOnUpEvent = true;
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
- mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
- boolean scrollersFinished = mFlingScroller.isFinished()
- && mAdjustScroller.isFinished();
- if (!scrollersFinished) {
- mFlingScroller.forceFinished(true);
- mAdjustScroller.forceFinished(true);
- onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- }
- mCheckBeginEditOnUpEvent = scrollersFinished;
- mAdjustScrollerOnUpEvent = true;
+ mInputText.setVisibility(View.INVISIBLE);
+ mLastDownOrMoveEventY = mLastDownEventY = event.getY();
+ mLastDownEventTime = event.getEventTime();
+ mIngonreMoveEvents = false;
+ mShowSoftInputOnTap = false;
+ if (!mFlingScroller.isFinished()) {
+ mFlingScroller.forceFinished(true);
+ mAdjustScroller.forceFinished(true);
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ } else if (!mAdjustScroller.isFinished()) {
+ mFlingScroller.forceFinished(true);
+ mAdjustScroller.forceFinished(true);
+ } else if (mLastDownEventY < mTopSelectionDividerTop) {
hideSoftInput();
- hideInputControls();
- return true;
- }
- if (isEventInVisibleViewHitRect(event, mIncrementButton)
- || isEventInVisibleViewHitRect(event, mDecrementButton)) {
- return false;
- }
- mAdjustScrollerOnUpEvent = false;
- setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
- hideSoftInput();
- hideInputControls();
- return true;
- case MotionEvent.ACTION_MOVE:
- float currentMoveY = event.getY();
- int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
- if (deltaDownY > mTouchSlop) {
- mCheckBeginEditOnUpEvent = false;
- onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
- setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
+ postChangeCurrentByOneFromLongPress(
+ false, ViewConfiguration.getLongPressTimeout());
+ } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
hideSoftInput();
- hideInputControls();
- return true;
+ postChangeCurrentByOneFromLongPress(
+ true, ViewConfiguration.getLongPressTimeout());
+ } else {
+ mShowSoftInputOnTap = true;
+ postBeginSoftInputOnLongPressCommand();
}
- break;
+ return true;
+ }
}
return false;
}
@Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (!isEnabled()) {
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled() || !mHasSelectorWheel) {
return false;
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
- mVelocityTracker.addMovement(ev);
- int action = ev.getActionMasked();
+ mVelocityTracker.addMovement(event);
+ int action = event.getActionMasked();
switch (action) {
- case MotionEvent.ACTION_MOVE:
- float currentMoveY = ev.getY();
- if (mCheckBeginEditOnUpEvent
- || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ case MotionEvent.ACTION_MOVE: {
+ if (mIngonreMoveEvents) {
+ break;
+ }
+ float currentMoveY = event.getY();
+ if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
if (deltaDownY > mTouchSlop) {
- mCheckBeginEditOnUpEvent = false;
+ removeAllCallbacks();
onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
+ } else {
+ int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY)
+ / TOUCH_SCROLL_DECELERATION_COEFFICIENT);
+ scrollBy(0, deltaMoveY);
+ invalidate();
}
- int deltaMoveY = (int) (currentMoveY - mLastMotionEventY);
- scrollBy(0, deltaMoveY);
- invalidate();
- mLastMotionEventY = currentMoveY;
- break;
- case MotionEvent.ACTION_UP:
- if (mCheckBeginEditOnUpEvent) {
- mCheckBeginEditOnUpEvent = false;
- final long deltaTapTimeMillis = ev.getEventTime() - mLastUpEventTimeMillis;
- if (deltaTapTimeMillis < ViewConfiguration.getDoubleTapTimeout()) {
- setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
- showInputControls(mShowInputControlsAnimimationDuration);
- mInputText.requestFocus();
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
- if (inputMethodManager != null) {
- inputMethodManager.showSoftInput(mInputText, 0);
- }
- mLastUpEventTimeMillis = ev.getEventTime();
- return true;
- }
- }
+ mLastDownOrMoveEventY = currentMoveY;
+ } break;
+ case MotionEvent.ACTION_UP: {
+ removeBeginSoftInputCommand();
+ removeChangeCurrentByOneFromLongPress();
VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity();
if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
- fling(initialVelocity);
+ int deltaMove = (int) (event.getY() - mLastDownEventY);
+ int absDeltaMoveY = Math.abs(deltaMove);
+ if (absDeltaMoveY > mMinFlingDistance) {
+ fling(initialVelocity);
+ } else {
+ changeValueByOne(deltaMove < 0);
+ }
onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
} else {
- if (mAdjustScrollerOnUpEvent) {
- if (mFlingScroller.isFinished() && mAdjustScroller.isFinished()) {
- postAdjustScrollerCommand(0);
+ int eventY = (int) event.getY();
+ int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
+ long deltaTime = event.getEventTime() - mLastDownEventTime;
+ if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) {
+ if (mShowSoftInputOnTap) {
+ mShowSoftInputOnTap = false;
+ showSoftInput();
+ } else {
+ int selectorIndexOffset = (eventY / mSelectorElementHeight)
+ - SELECTOR_MIDDLE_ITEM_INDEX;
+ if (selectorIndexOffset > 0) {
+ changeValueByOne(true);
+ } else if (selectorIndexOffset < 0) {
+ changeValueByOne(false);
+ }
}
} else {
- postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS);
+ ensureScrollWheelAdjusted();
}
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
- mLastUpEventTimeMillis = ev.getEventTime();
- break;
+ } break;
}
return true;
}
@@ -891,12 +874,6 @@ public class NumberPicker extends LinearLayout {
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
switch (action) {
- case MotionEvent.ACTION_MOVE:
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
- removeAllCallbacks();
- forceCompleteChangeCurrentByOneViaScroll();
- }
- break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
removeAllCallbacks();
@@ -907,27 +884,75 @@ public class NumberPicker extends LinearLayout {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- int keyCode = event.getKeyCode();
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
- removeAllCallbacks();
+ final int keyCode = event.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ removeAllCallbacks();
+ break;
}
return super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- removeAllCallbacks();
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ removeAllCallbacks();
+ break;
}
return super.dispatchTrackballEvent(event);
}
@Override
- public void computeScroll() {
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
- return;
+ protected boolean dispatchHoverEvent(MotionEvent event) {
+ if (!mHasSelectorWheel) {
+ return super.dispatchHoverEvent(event);
+ }
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ final int eventY = (int) event.getY();
+ final int hoveredVirtualViewId;
+ if (eventY < mTopSelectionDividerTop) {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_DECREMENT;
+ } else if (eventY > mBottomSelectionDividerBottom) {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INCREMENT;
+ } else {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT;
+ }
+ final int action = event.getActionMasked();
+ AccessibilityNodeProviderImpl provider =
+ (AccessibilityNodeProviderImpl) getAccessibilityNodeProvider();
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER: {
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ } break;
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
+ && mLastHoveredChildVirtualViewId != View.NO_ID) {
+ provider.sendAccessibilityEventForVirtualView(
+ mLastHoveredChildVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ }
+ } break;
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ mLastHoveredChildVirtualViewId = View.NO_ID;
+ } break;
+ }
}
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
Scroller scroller = mFlingScroller;
if (scroller.isFinished()) {
scroller = mAdjustScroller;
@@ -952,16 +977,17 @@ public class NumberPicker extends LinearLayout {
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
- mIncrementButton.setEnabled(enabled);
- mDecrementButton.setEnabled(enabled);
+ if (!mHasSelectorWheel) {
+ mIncrementButton.setEnabled(enabled);
+ }
+ if (!mHasSelectorWheel) {
+ mDecrementButton.setEnabled(enabled);
+ }
mInputText.setEnabled(enabled);
}
@Override
public void scrollBy(int x, int y) {
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
- return;
- }
int[] selectorIndices = mSelectorIndices;
if (!mWrapSelectorWheel && y > 0
&& selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
@@ -977,7 +1003,7 @@ public class NumberPicker extends LinearLayout {
while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
mCurrentScrollOffset -= mSelectorElementHeight;
decrementSelectorIndices(selectorIndices);
- changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
+ setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOffset = mInitialScrollOffset;
}
@@ -985,7 +1011,7 @@ public class NumberPicker extends LinearLayout {
while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
mCurrentScrollOffset += mSelectorElementHeight;
incrementSelectorIndices(selectorIndices);
- changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
+ setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
mCurrentScrollOffset = mInitialScrollOffset;
}
@@ -1024,8 +1050,7 @@ public class NumberPicker extends LinearLayout {
*
* @param formatter The formatter object. If formatter is <code>null</code>,
* {@link String#valueOf(int)} will be used.
- *
- * @see #setDisplayedValues(String[])
+ *@see #setDisplayedValues(String[])
*/
public void setFormatter(Formatter formatter) {
if (formatter == mFormatter) {
@@ -1068,26 +1093,35 @@ public class NumberPicker extends LinearLayout {
if (mValue == value) {
return;
}
- if (value < mMinValue) {
- value = mWrapSelectorWheel ? mMaxValue : mMinValue;
- }
- if (value > mMaxValue) {
- value = mWrapSelectorWheel ? mMinValue : mMaxValue;
- }
- mValue = value;
+ setValueInternal(value, false);
initializeSelectorWheelIndices();
- updateInputTextView();
- updateIncrementAndDecrementButtonsVisibilityState();
invalidate();
}
/**
- * Hides the soft input of it is active for the input text.
+ * Shows the soft input for its input text.
+ */
+ private void showSoftInput() {
+ InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ if (inputMethodManager != null) {
+ if (mHasSelectorWheel) {
+ mInputText.setVisibility(View.VISIBLE);
+ }
+ mInputText.requestFocus();
+ inputMethodManager.showSoftInput(mInputText, 0);
+ }
+ }
+
+ /**
+ * Hides the soft input if it is active for the input text.
*/
private void hideSoftInput() {
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ if (mHasSelectorWheel) {
+ mInputText.setVisibility(View.INVISIBLE);
+ }
}
}
@@ -1151,23 +1185,22 @@ public class NumberPicker extends LinearLayout {
* wrap around the {@link NumberPicker#getMinValue()} and
* {@link NumberPicker#getMaxValue()} values.
* <p>
- * By default if the range (max - min) is more than five (the number of
- * items shown on the selector wheel) the selector wheel wrapping is
- * enabled.
+ * By default if the range (max - min) is more than the number of items shown
+ * on the selector wheel the selector wheel wrapping is enabled.
* </p>
* <p>
- * <strong>Note:</strong> If the number of items, i.e. the range
- * ({@link #getMaxValue()} - {@link #getMinValue()}) is less than
- * {@link #SELECTOR_WHEEL_ITEM_COUNT}, the selector wheel will not
- * wrap. Hence, in such a case calling this method is a NOP.
+ * <strong>Note:</strong> If the number of items, i.e. the range (
+ * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
+ * the number of items shown on the selector wheel, the selector wheel will
+ * not wrap. Hence, in such a case calling this method is a NOP.
* </p>
+ *
* @param wrapSelectorWheel Whether to wrap.
*/
public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
mWrapSelectorWheel = wrapSelectorWheel;
- updateIncrementAndDecrementButtonsVisibilityState();
}
}
@@ -1224,6 +1257,7 @@ public class NumberPicker extends LinearLayout {
initializeSelectorWheelIndices();
updateInputTextView();
tryComputeMaxWidth();
+ invalidate();
}
/**
@@ -1256,6 +1290,7 @@ public class NumberPicker extends LinearLayout {
initializeSelectorWheelIndices();
updateInputTextView();
tryComputeMaxWidth();
+ invalidate();
}
/**
@@ -1300,102 +1335,49 @@ public class NumberPicker extends LinearLayout {
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- // make sure we show the controls only the very
- // first time the user sees this widget
- if (mFlingable && !isInEditMode()) {
- // animate a bit slower the very first time
- showInputControls(mShowInputControlsAnimimationDuration * 2);
- }
- }
-
- @Override
protected void onDetachedFromWindow() {
removeAllCallbacks();
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- // There is a good reason for doing this. See comments in draw().
- }
-
- @Override
- public void draw(Canvas canvas) {
- // Dispatch draw to our children only if we are not currently running
- // the animation for simultaneously dimming the scroll wheel and
- // showing in the buttons. This class takes advantage of the View
- // implementation of fading edges effect to draw the selector wheel.
- // However, in View.draw(), the fading is applied after all the children
- // have been drawn and we do not want this fading to be applied to the
- // buttons. Therefore, we draw our children after we have completed
- // drawing ourselves.
- super.draw(canvas);
-
- // Draw our children if we are not showing the selector wheel of fading
- // it out
- if (mShowInputControlsAnimator.isRunning()
- || mSelectorWheelState != SELECTOR_WHEEL_STATE_LARGE) {
- long drawTime = getDrawingTime();
- for (int i = 0, count = getChildCount(); i < count; i++) {
- View child = getChildAt(i);
- if (!child.isShown()) {
- continue;
- }
- drawChild(canvas, getChildAt(i), drawTime);
- }
- }
- }
-
- @Override
protected void onDraw(Canvas canvas) {
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
+ if (!mHasSelectorWheel) {
+ super.onDraw(canvas);
return;
}
-
float x = (mRight - mLeft) / 2;
float y = mCurrentScrollOffset;
- final int restoreCount = canvas.save();
-
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_SMALL) {
- Rect clipBounds = canvas.getClipBounds();
- clipBounds.inset(0, mSelectorElementHeight);
- canvas.clipRect(clipBounds);
- }
-
// draw the selector wheel
int[] selectorIndices = mSelectorIndices;
for (int i = 0; i < selectorIndices.length; i++) {
int selectorIndex = selectorIndices[i];
String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
- // Do not draw the middle item if input is visible since the input is shown only
- // if the wheel is static and it covers the middle item. Otherwise, if the user
- // starts editing the text via the IME he may see a dimmed version of the old
- // value intermixed with the new one.
+ // Do not draw the middle item if input is visible since the input
+ // is shown only if the wheel is static and it covers the middle
+ // item. Otherwise, if the user starts editing the text via the
+ // IME he may see a dimmed version of the old value intermixed
+ // with the new one.
if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
}
y += mSelectorElementHeight;
}
- // draw the selection dividers (only if scrolling and drawable specified)
+ // draw the selection dividers
if (mSelectionDivider != null) {
// draw the top divider
- int topOfTopDivider =
- (getHeight() - mSelectorElementHeight - mSelectionDividerHeight) / 2;
+ int topOfTopDivider = mTopSelectionDividerTop;
int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
mSelectionDivider.draw(canvas);
// draw the bottom divider
- int topOfBottomDivider = topOfTopDivider + mSelectorElementHeight;
- int bottomOfBottomDivider = bottomOfTopDivider + mSelectorElementHeight;
+ int bottomOfBottomDivider = mBottomSelectionDividerBottom;
+ int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
mSelectionDivider.draw(canvas);
}
-
- canvas.restoreToCount(restoreCount);
}
@Override
@@ -1408,12 +1390,20 @@ public class NumberPicker extends LinearLayout {
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(NumberPicker.class.getName());
+ event.setScrollable(true);
+ event.setScrollY((mMinValue + mValue) * mSelectorElementHeight);
+ event.setMaxScrollY((mMaxValue - mMinValue) * mSelectorElementHeight);
}
@Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- info.setClassName(NumberPicker.class.getName());
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ if (!mHasSelectorWheel) {
+ return super.getAccessibilityNodeProvider();
+ }
+ if (mAccessibilityNodeProvider == null) {
+ mAccessibilityNodeProvider = new AccessibilityNodeProviderImpl();
+ }
+ return mAccessibilityNodeProvider;
}
/**
@@ -1442,17 +1432,17 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Utility to reconcile a desired size and state, with constraints imposed by
- * a MeasureSpec. Tries to respect the min size, unless a different size is
- * imposed by the constraints.
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Tries to respect the min size, unless a different size
+ * is imposed by the constraints.
*
* @param minSize The minimal desired size.
* @param measuredSize The currently measured size.
* @param measureSpec The current measure spec.
* @return The resolved size and state.
*/
- private int resolveSizeAndStateRespectingMinSize(int minSize, int measuredSize,
- int measureSpec) {
+ private int resolveSizeAndStateRespectingMinSize(
+ int minSize, int measuredSize, int measureSpec) {
if (minSize != SIZE_UNSPECIFIED) {
final int desiredWidth = Math.max(minSize, measuredSize);
return resolveSizeAndState(desiredWidth, measureSpec, 0);
@@ -1462,8 +1452,8 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Resets the selector indices and clear the cached
- * string representation of these indices.
+ * Resets the selector indices and clear the cached string representation of
+ * these indices.
*/
private void initializeSelectorWheelIndices() {
mSelectorIndexToStringCache.clear();
@@ -1480,39 +1470,44 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Sets the current value of this NumberPicker, and sets mPrevious to the
- * previous value. If current is greater than mEnd less than mStart, the
- * value of mCurrent is wrapped around. Subclasses can override this to
- * change the wrapping behavior
+ * Sets the current value of this NumberPicker.
*
- * @param current the new value of the NumberPicker
+ * @param current The new value of the NumberPicker.
+ * @param notifyChange Whether to notify if the current value changed.
*/
- private void changeCurrent(int current) {
+ private void setValueInternal(int current, boolean notifyChange) {
if (mValue == current) {
return;
}
// Wrap around the values if we go past the start or end
if (mWrapSelectorWheel) {
current = getWrappedSelectorIndex(current);
+ } else {
+ current = Math.max(current, mMinValue);
+ current = Math.min(current, mMaxValue);
}
int previous = mValue;
- setValue(current);
- notifyChange(previous, current);
+ mValue = current;
+ updateInputTextView();
+ if (notifyChange) {
+ notifyChange(previous, current);
+ }
}
/**
* Changes the current value by one which is increment or
* decrement based on the passes argument.
+ * decrement the current value.
*
* @param increment True to increment, false to decrement.
*/
- private void changeCurrentByOne(boolean increment) {
- if (mFlingable) {
- mDimSelectorWheelAnimator.cancel();
+ private void changeValueByOne(boolean increment) {
+ if (mHasSelectorWheel) {
mInputText.setVisibility(View.INVISIBLE);
- mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
+ if (!moveToFinalScrollerPosition(mFlingScroller)) {
+ moveToFinalScrollerPosition(mAdjustScroller);
+ }
mPreviousScrollerY = 0;
- forceCompleteChangeCurrentByOneViaScroll();
if (increment) {
mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight,
CHANGE_CURRENT_BY_ONE_SCROLL_DURATION);
@@ -1523,81 +1518,26 @@ public class NumberPicker extends LinearLayout {
invalidate();
} else {
if (increment) {
- changeCurrent(mValue + 1);
+ setValueInternal(mValue + 1, true);
} else {
- changeCurrent(mValue - 1);
+ setValueInternal(mValue - 1, true);
}
}
}
- /**
- * Ensures that if we are in the process of changing the current value
- * by one via scrolling the scroller gets to its final state and the
- * value is updated.
- */
- private void forceCompleteChangeCurrentByOneViaScroll() {
- Scroller scroller = mFlingScroller;
- if (!scroller.isFinished()) {
- final int yBeforeAbort = scroller.getCurrY();
- scroller.abortAnimation();
- final int yDelta = scroller.getCurrY() - yBeforeAbort;
- scrollBy(0, yDelta);
- }
- }
-
- /**
- * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector
- * wheel.
- */
- @SuppressWarnings("unused")
- // Called via reflection
- private void setSelectorPaintAlpha(int alpha) {
- mSelectorWheelPaint.setAlpha(alpha);
- invalidate();
- }
-
- /**
- * @return If the <code>event</code> is in the visible <code>view</code>.
- */
- private boolean isEventInVisibleViewHitRect(MotionEvent event, View view) {
- if (view.getVisibility() == VISIBLE) {
- view.getHitRect(mTempRect);
- return mTempRect.contains((int) event.getX(), (int) event.getY());
- }
- return false;
- }
-
- /**
- * Sets the <code>selectorWheelState</code>.
- */
- private void setSelectorWheelState(int selectorWheelState) {
- mSelectorWheelState = selectorWheelState;
- if (selectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
- mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
- }
-
- if (mFlingable && selectorWheelState == SELECTOR_WHEEL_STATE_LARGE
- && AccessibilityManager.getInstance(mContext).isEnabled()) {
- AccessibilityManager.getInstance(mContext).interrupt();
- String text = mContext.getString(R.string.number_picker_increment_scroll_action);
- mInputText.setContentDescription(text);
- mInputText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- mInputText.setContentDescription(null);
- }
- }
-
private void initializeSelectorWheel() {
initializeSelectorWheelIndices();
int[] selectorIndices = mSelectorIndices;
int totalTextHeight = selectorIndices.length * mTextSize;
float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
- float textGapCount = selectorIndices.length - 1;
+ float textGapCount = selectorIndices.length;
mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
- // Ensure that the middle item is positioned the same as the text in mInputText
+ // Ensure that the middle item is positioned the same as the text in
+ // mInputText
int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
- mInitialScrollOffset = editTextTextPosition -
- (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
+ mInitialScrollOffset = editTextTextPosition
+ - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
mCurrentScrollOffset = mInitialScrollOffset;
updateInputTextView();
}
@@ -1612,16 +1552,14 @@ public class NumberPicker extends LinearLayout {
*/
private void onScrollerFinished(Scroller scroller) {
if (scroller == mFlingScroller) {
- if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
- postAdjustScrollerCommand(0);
- onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- } else {
+ if (!ensureScrollWheelAdjusted()) {
updateInputTextView();
- fadeSelectorWheel(mShowInputControlsAnimimationDuration);
}
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
} else {
- updateInputTextView();
- showInputControls(mShowInputControlsAnimimationDuration);
+ if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ updateInputTextView();
+ }
}
}
@@ -1654,56 +1592,6 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Hides the input controls which is the up/down arrows and the text field.
- */
- private void hideInputControls() {
- mShowInputControlsAnimator.cancel();
- mIncrementButton.setVisibility(INVISIBLE);
- mDecrementButton.setVisibility(INVISIBLE);
- mInputText.setVisibility(INVISIBLE);
- }
-
- /**
- * Show the input controls by making them visible and animating the alpha
- * property up/down arrows.
- *
- * @param animationDuration The duration of the animation.
- */
- private void showInputControls(long animationDuration) {
- updateIncrementAndDecrementButtonsVisibilityState();
- mInputText.setVisibility(VISIBLE);
- mShowInputControlsAnimator.setDuration(animationDuration);
- mShowInputControlsAnimator.start();
- }
-
- /**
- * Fade the selector wheel via an animation.
- *
- * @param animationDuration The duration of the animation.
- */
- private void fadeSelectorWheel(long animationDuration) {
- mInputText.setVisibility(VISIBLE);
- mDimSelectorWheelAnimator.setDuration(animationDuration);
- mDimSelectorWheelAnimator.start();
- }
-
- /**
- * Updates the visibility state of the increment and decrement buttons.
- */
- private void updateIncrementAndDecrementButtonsVisibilityState() {
- if (mWrapSelectorWheel || mValue < mMaxValue) {
- mIncrementButton.setVisibility(VISIBLE);
- } else {
- mIncrementButton.setVisibility(INVISIBLE);
- }
- if (mWrapSelectorWheel || mValue > mMinValue) {
- mDecrementButton.setVisibility(VISIBLE);
- } else {
- mDecrementButton.setVisibility(INVISIBLE);
- }
- }
-
- /**
* @return The wrapped index <code>selectorIndex</code> value.
*/
private int getWrappedSelectorIndex(int selectorIndex) {
@@ -1749,8 +1637,7 @@ public class NumberPicker extends LinearLayout {
/**
* Ensures we have a cached string representation of the given <code>
- * selectorIndex</code>
- * to avoid multiple instantiations of the same string.
+ * selectorIndex</code> to avoid multiple instantiations of the same string.
*/
private void ensureCachedScrollSelectorValue(int selectorIndex) {
SparseArray<String> cache = mSelectorIndexToStringCache;
@@ -1783,7 +1670,7 @@ public class NumberPicker extends LinearLayout {
} else {
// Check the new value and ensure it's in range
int current = getSelectedPos(str.toString());
- changeCurrent(current);
+ setValueInternal(current, true);
}
}
@@ -1792,25 +1679,23 @@ public class NumberPicker extends LinearLayout {
* the string corresponding to the index specified by the current value will
* be returned. Otherwise, the formatter specified in {@link #setFormatter}
* will be used to format the number.
+ *
+ * @return Whether the text was updated.
*/
- private void updateInputTextView() {
+ private boolean updateInputTextView() {
/*
* If we don't have displayed values then use the current number else
* find the correct value in the displayed values for the current
* number.
*/
- if (mDisplayedValues == null) {
- mInputText.setText(formatNumber(mValue));
- } else {
- mInputText.setText(mDisplayedValues[mValue - mMinValue]);
+ String text = (mDisplayedValues == null) ? formatNumber(mValue)
+ : mDisplayedValues[mValue - mMinValue];
+ if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
+ mInputText.setText(text);
+ return true;
}
- mInputText.setSelection(mInputText.getText().length());
- if (mFlingable && AccessibilityManager.getInstance(mContext).isEnabled()) {
- String text = mContext.getString(R.string.number_picker_increment_scroll_mode,
- mInputText.getText());
- mInputText.setContentDescription(text);
- }
+ return false;
}
/**
@@ -1828,14 +1713,45 @@ public class NumberPicker extends LinearLayout {
*
* @param increment Whether to increment or decrement the value.
*/
- private void postChangeCurrentByOneFromLongPress(boolean increment) {
- mInputText.clearFocus();
- removeAllCallbacks();
+ private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) {
if (mChangeCurrentByOneFromLongPressCommand == null) {
mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
+ } else {
+ removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
+ }
+ mChangeCurrentByOneFromLongPressCommand.setStep(increment);
+ postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis);
+ }
+
+ /**
+ * Removes the command for changing the current value by one.
+ */
+ private void removeChangeCurrentByOneFromLongPress() {
+ if (mChangeCurrentByOneFromLongPressCommand != null) {
+ removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
+ }
+ }
+
+ /**
+ * Posts a command for beginning an edit of the current value via IME on
+ * long press.
+ */
+ private void postBeginSoftInputOnLongPressCommand() {
+ if (mBeginSoftInputOnLongPressCommand == null) {
+ mBeginSoftInputOnLongPressCommand = new BeginSoftInputOnLongPressCommand();
+ } else {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
+ }
+ postDelayed(mBeginSoftInputOnLongPressCommand, ViewConfiguration.getLongPressTimeout());
+ }
+
+ /**
+ * Removes the command for beginning an edit of the current value via IME.
+ */
+ private void removeBeginSoftInputCommand() {
+ if (mBeginSoftInputOnLongPressCommand != null) {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
}
- mChangeCurrentByOneFromLongPressCommand.setIncrement(increment);
- post(mChangeCurrentByOneFromLongPressCommand);
}
/**
@@ -1845,12 +1761,12 @@ public class NumberPicker extends LinearLayout {
if (mChangeCurrentByOneFromLongPressCommand != null) {
removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
}
- if (mAdjustScrollerCommand != null) {
- removeCallbacks(mAdjustScrollerCommand);
- }
if (mSetSelectionCommand != null) {
removeCallbacks(mSetSelectionCommand);
}
+ if (mBeginSoftInputOnLongPressCommand != null) {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
+ }
}
/**
@@ -1888,8 +1804,7 @@ public class NumberPicker extends LinearLayout {
/**
* Posts an {@link SetSelectionCommand} from the given <code>selectionStart
- * </code> to
- * <code>selectionEnd</code>.
+ * </code> to <code>selectionEnd</code>.
*/
private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
if (mSetSelectionCommand == null) {
@@ -1903,20 +1818,6 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Posts an {@link AdjustScrollerCommand} within the given <code>
- * delayMillis</code>
- * .
- */
- private void postAdjustScrollerCommand(int delayMillis) {
- if (mAdjustScrollerCommand == null) {
- mAdjustScrollerCommand = new AdjustScrollerCommand();
- } else {
- removeCallbacks(mAdjustScrollerCommand);
- }
- postDelayed(mAdjustScrollerCommand, delayMillis);
- }
-
- /**
* Filter for accepting only valid indices or prefixes of the string
* representation of valid indices.
*/
@@ -1934,8 +1835,8 @@ public class NumberPicker extends LinearLayout {
}
@Override
- public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
- int dstart, int dend) {
+ public CharSequence filter(
+ CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (mDisplayedValues == null) {
CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
if (filtered == null) {
@@ -1981,6 +1882,27 @@ public class NumberPicker extends LinearLayout {
}
/**
+ * Ensures that the scroll wheel is adjusted i.e. there is no offset and the
+ * middle element is in the middle of the widget.
+ *
+ * @return Whether an adjustment has been made.
+ */
+ private boolean ensureScrollWheelAdjusted() {
+ // adjust to the closest value
+ int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
+ if (deltaY != 0) {
+ mPreviousScrollerY = 0;
+ if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
+ deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
+ }
+ mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
+ invalidate();
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Command for setting the input text selection.
*/
class SetSelectionCommand implements Runnable {
@@ -1994,39 +1916,18 @@ public class NumberPicker extends LinearLayout {
}
/**
- * Command for adjusting the scroller to show in its center the closest of
- * the displayed items.
- */
- class AdjustScrollerCommand implements Runnable {
- public void run() {
- mPreviousScrollerY = 0;
- if (mInitialScrollOffset == mCurrentScrollOffset) {
- updateInputTextView();
- showInputControls(mShowInputControlsAnimimationDuration);
- return;
- }
- // adjust to the closest value
- int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
- if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
- deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
- }
- mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
- invalidate();
- }
- }
-
- /**
* Command for changing the current value from a long press by one.
*/
class ChangeCurrentByOneFromLongPressCommand implements Runnable {
private boolean mIncrement;
- private void setIncrement(boolean increment) {
+ private void setStep(boolean increment) {
mIncrement = increment;
}
+ @Override
public void run() {
- changeCurrentByOne(mIncrement);
+ changeValueByOne(mIncrement);
postDelayed(this, mLongPressUpdateInterval);
}
}
@@ -2048,4 +1949,248 @@ public class NumberPicker extends LinearLayout {
}
}
}
+
+ /**
+ * Command for beginning soft input on long press.
+ */
+ class BeginSoftInputOnLongPressCommand implements Runnable {
+
+ @Override
+ public void run() {
+ showSoftInput();
+ mIngonreMoveEvents = true;
+ }
+ }
+
+ class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
+ private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
+
+ private static final int VIRTUAL_VIEW_ID_INPUT = 2;
+
+ private static final int VIRTUAL_VIEW_ID_DECREMENT = 3;
+
+ private final Rect mTempRect = new Rect();
+
+ private final int[] mTempArray = new int[2];
+
+ @Override
+ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ switch (virtualViewId) {
+ case View.NO_ID:
+ return createAccessibilityNodeInfoForNumberPicker( mScrollX, mScrollY,
+ mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
+ case VIRTUAL_VIEW_ID_DECREMENT:
+ return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_DECREMENT,
+ getVirtualDecrementButtonText(), mScrollX, mScrollY,
+ mScrollX + (mRight - mLeft),
+ mTopSelectionDividerTop + mSelectionDividerHeight);
+ case VIRTUAL_VIEW_ID_INPUT:
+ return createAccessibiltyNodeInfoForInputText();
+ case VIRTUAL_VIEW_ID_INCREMENT:
+ return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_INCREMENT,
+ getVirtualIncrementButtonText(), mScrollX,
+ mBottomSelectionDividerBottom - mSelectionDividerHeight,
+ mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
+ }
+ return super.createAccessibilityNodeInfo(virtualViewId);
+ }
+
+ @Override
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
+ int virtualViewId) {
+ if (TextUtils.isEmpty(searched)) {
+ return Collections.emptyList();
+ }
+ String searchedLowerCase = searched.toLowerCase();
+ List<AccessibilityNodeInfo> result = new ArrayList<AccessibilityNodeInfo>();
+ switch (virtualViewId) {
+ case View.NO_ID: {
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_DECREMENT, result);
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_INPUT, result);
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_INCREMENT, result);
+ return result;
+ }
+ case VIRTUAL_VIEW_ID_DECREMENT:
+ case VIRTUAL_VIEW_ID_INCREMENT:
+ case VIRTUAL_VIEW_ID_INPUT: {
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase, virtualViewId,
+ result);
+ return result;
+ }
+ }
+ return super.findAccessibilityNodeInfosByText(searched, virtualViewId);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, int virtualViewId) {
+ switch (virtualViewId) {
+ case VIRTUAL_VIEW_ID_INPUT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_FOCUS: {
+ if (!mInputText.isFocused()) {
+ return mInputText.requestFocus();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+ if (mInputText.isFocused()) {
+ mInputText.clearFocus();
+ return true;
+ }
+ } break;
+ }
+ } break;
+ }
+ return super.performAccessibilityAction(action, virtualViewId);
+ }
+
+ public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
+ switch (virtualViewId) {
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualDecrementButtonText());
+ } break;
+ case VIRTUAL_VIEW_ID_INPUT: {
+ sendAccessibilityEventForVirtualText(eventType);
+ } break;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualIncrementButtonText());
+ } break;
+ }
+ }
+
+ private void sendAccessibilityEventForVirtualText(int eventType) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ mInputText.onInitializeAccessibilityEvent(event);
+ mInputText.onPopulateAccessibilityEvent(event);
+ event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
+
+ private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
+ String text) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setClassName(Button.class.getName());
+ event.setPackageName(mContext.getPackageName());
+ event.getText().add(text);
+ event.setEnabled(NumberPicker.this.isEnabled());
+ event.setSource(NumberPicker.this, virtualViewId);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
+
+ private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
+ int virtualViewId, List<AccessibilityNodeInfo> outResult) {
+ switch (virtualViewId) {
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ String text = getVirtualDecrementButtonText();
+ if (!TextUtils.isEmpty(text)
+ && text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT));
+ }
+ } return;
+ case VIRTUAL_VIEW_ID_INPUT: {
+ CharSequence text = mInputText.getText();
+ if (!TextUtils.isEmpty(text) &&
+ text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
+ return;
+ }
+ CharSequence contentDesc = mInputText.getText();
+ if (!TextUtils.isEmpty(contentDesc) &&
+ contentDesc.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
+ return;
+ }
+ } break;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ String text = getVirtualIncrementButtonText();
+ if (!TextUtils.isEmpty(text)
+ && text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT));
+ }
+ } return;
+ }
+ }
+
+ private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
+ AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
+ info.setLongClickable(true);
+ info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ return info;
+ }
+
+ private AccessibilityNodeInfo createAccessibilityNodeInfoForVirtualButton(int virtualViewId,
+ String text, int left, int top, int right, int bottom) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setClassName(Button.class.getName());
+ info.setPackageName(mContext.getPackageName());
+ info.setSource(NumberPicker.this, virtualViewId);
+ info.setParent(NumberPicker.this);
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
+ info.setText(text);
+ info.setClickable(true);
+ info.setLongClickable(true);
+ info.setEnabled(NumberPicker.this.isEnabled());
+ Rect boundsInParent = mTempRect;
+ boundsInParent.set(left, top, right, bottom);
+ info.setBoundsInParent(boundsInParent);
+ Rect boundsInScreen = boundsInParent;
+ int[] locationOnScreen = mTempArray;
+ getLocationOnScreen(locationOnScreen);
+ boundsInScreen.offsetTo(0, 0);
+ boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
+ info.setBoundsInScreen(boundsInScreen);
+ return info;
+ }
+
+ private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int left, int top,
+ int right, int bottom) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setClassName(Button.class.getName());
+ info.setPackageName(mContext.getPackageName());
+ info.setSource(NumberPicker.this);
+ info.setParent((View) getParent());
+ info.setEnabled(NumberPicker.this.isEnabled());
+ info.setScrollable(true);
+ Rect boundsInParent = mTempRect;
+ boundsInParent.set(left, top, right, bottom);
+ info.setBoundsInParent(boundsInParent);
+ Rect boundsInScreen = boundsInParent;
+ int[] locationOnScreen = mTempArray;
+ getLocationOnScreen(locationOnScreen);
+ boundsInScreen.offsetTo(0, 0);
+ boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
+ info.setBoundsInScreen(boundsInScreen);
+ return info;
+ }
+
+ private String getVirtualDecrementButtonText() {
+ int value = mValue - 1;
+ if (mWrapSelectorWheel) {
+ value = getWrappedSelectorIndex(value);
+ }
+ if (value >= mMinValue) {
+ return (mDisplayedValues == null) ? formatNumber(value)
+ : mDisplayedValues[value - mMinValue];
+ }
+ return null;
+ }
+
+ private String getVirtualIncrementButtonText() {
+ int value = mValue + 1;
+ if (mWrapSelectorWheel) {
+ value = getWrappedSelectorIndex(value);
+ }
+ if (value <= mMaxValue) {
+ return (mDisplayedValues == null) ? formatNumber(value)
+ : mDisplayedValues[value - mMinValue];
+ }
+ return null;
+ }
+ }
}
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 7f53ffd..f217c9c 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -190,6 +190,8 @@ public class RadioGroup extends LinearLayout {
*
* @see #check(int)
* @see #clearCheck()
+ *
+ * @attr ref android.R.styleable#RadioGroup_checkedButton
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index e69577b..524d272 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -145,6 +145,8 @@ public class RatingBar extends AbsSeekBar {
* by the user).
*
* @param isIndicator Whether it should be an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public void setIsIndicator(boolean isIndicator) {
mIsUserSeekable = !isIndicator;
@@ -153,6 +155,8 @@ public class RatingBar extends AbsSeekBar {
/**
* @return Whether this rating bar is only an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public boolean isIndicator() {
return !mIsUserSeekable;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 55acb74..2f72e4a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -965,6 +965,58 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Helper action to set compound drawables on a TextView. Supports relative
+ * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
+ */
+ private class TextViewDrawableAction extends Action {
+ public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
+ this.viewId = viewId;
+ this.isRelative = isRelative;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.d4 = d4;
+ }
+
+ public TextViewDrawableAction(Parcel parcel) {
+ viewId = parcel.readInt();
+ isRelative = (parcel.readInt() != 0);
+ d1 = parcel.readInt();
+ d2 = parcel.readInt();
+ d3 = parcel.readInt();
+ d4 = parcel.readInt();
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ dest.writeInt(isRelative ? 1 : 0);
+ dest.writeInt(d1);
+ dest.writeInt(d2);
+ dest.writeInt(d3);
+ dest.writeInt(d4);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent) {
+ final Context context = root.getContext();
+ final TextView target = (TextView) root.findViewById(viewId);
+ if (target == null) return;
+ if (isRelative) {
+ target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
+ } else {
+ target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
+ }
+ }
+
+ int viewId;
+ boolean isRelative = false;
+ int d1, d2, d3, d4;
+
+ public final static int TAG = 11;
+ }
+
+ /**
* Simple class used to keep track of memory usage in a RemoteViews.
*
*/
@@ -1043,6 +1095,9 @@ public class RemoteViews implements Parcelable, Filter {
case SetRemoteViewsAdapterIntent.TAG:
mActions.add(new SetRemoteViewsAdapterIntent(parcel));
break;
+ case TextViewDrawableAction.TAG:
+ mActions.add(new TextViewDrawableAction(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -1195,6 +1250,35 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling
+ * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param left The id of a drawable to place to the left of the text, or 0
+ * @param top The id of a drawable to place above the text, or 0
+ * @param right The id of a drawable to place to the right of the text, or 0
+ * @param bottom The id of a drawable to place below the text, or 0
+ */
+ public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
+ addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
+ }
+
+ /**
+ * Equivalent to calling {@link
+ * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param start The id of a drawable to place before the text (relative to the
+ * layout direction), or 0
+ * @param top The id of a drawable to place above the text, or 0
+ * @param end The id of a drawable to place after the text, or 0
+ * @param bottom The id of a drawable to place below the text, or 0
+ */
+ public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
+ addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
+ }
+
+ /**
* Equivalent to calling ImageView.setImageResource
*
* @param viewId The id of the view whose drawable should change
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 25dd438..e0e3e93 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -642,8 +642,7 @@ public class ScrollView extends FrameLayout {
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
- final float y = ev.getY(index);
- mLastMotionY = y;
+ mLastMotionY = ev.getY(index);
mActivePointerId = ev.getPointerId(index);
break;
}
@@ -715,6 +714,7 @@ public class ScrollView extends FrameLayout {
} else {
super.scrollTo(scrollX, scrollY);
}
+
awakenScrollBars();
}
@@ -749,42 +749,6 @@ public class ScrollView extends FrameLayout {
/**
* <p>
- * Finds the next focusable component that fits in this View's bounds
- * (excluding fading edges) pretending that this View's top is located at
- * the parameter top.
- * </p>
- *
- * @param topFocus look for a candidate at the top of the bounds if topFocus is true,
- * or at the bottom of the bounds if topFocus is false
- * @param top the top offset of the bounds in which a focusable must be
- * found (the fading edge is assumed to start at this position)
- * @param preferredFocusable the View that has highest priority and will be
- * returned if it is within my bounds (null is valid)
- * @return the next focusable component in the bounds or null if none can be found
- */
- private View findFocusableViewInMyBounds(final boolean topFocus,
- final int top, View preferredFocusable) {
- /*
- * The fading edge's transparent side should be considered for focus
- * since it's mostly visible, so we divide the actual fading edge length
- * by 2.
- */
- final int fadingEdgeLength = getVerticalFadingEdgeLength() / 2;
- final int topWithoutFadingEdge = top + fadingEdgeLength;
- final int bottomWithoutFadingEdge = top + getHeight() - fadingEdgeLength;
-
- if ((preferredFocusable != null)
- && (preferredFocusable.getTop() < bottomWithoutFadingEdge)
- && (preferredFocusable.getBottom() > topWithoutFadingEdge)) {
- return preferredFocusable;
- }
-
- return findFocusableViewInBounds(topFocus, topWithoutFadingEdge,
- bottomWithoutFadingEdge);
- }
-
- /**
- * <p>
* Finds the next focusable component that fits in the specified bounds.
* </p>
*
@@ -1208,10 +1172,10 @@ public class ScrollView extends FrameLayout {
}
}
- awakenScrollBars();
-
- // Keep on drawing until the animation has finished.
- postInvalidate();
+ if (!awakenScrollBars()) {
+ // Keep on drawing until the animation has finished.
+ invalidate();
+ }
} else {
if (mFlingStrictSpan != null) {
mFlingStrictSpan.finish();
@@ -1438,7 +1402,7 @@ public class ScrollView extends FrameLayout {
/**
* Return true if child is a descendant of parent, (or equal to the parent).
*/
- private boolean isViewDescendantOf(View child, View parent) {
+ private static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
@@ -1462,8 +1426,6 @@ public class ScrollView extends FrameLayout {
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);
- final boolean movingDown = velocityY > 0;
-
if (mFlingStrictSpan == null) {
mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
}
@@ -1554,7 +1516,7 @@ public class ScrollView extends FrameLayout {
}
}
- private int clamp(int n, int my, int child) {
+ private static int clamp(int n, int my, int child) {
if (my >= child || n < 0) {
/* my >= child is this case:
* |--------------- me ---------------|
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9941c95..1f2410b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1798,6 +1798,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableBottom
*/
+ @android.view.RemotableViewMethod
public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) {
final Resources resources = getContext().getResources();
setCompoundDrawablesWithIntrinsicBounds(left != 0 ? resources.getDrawable(left) : null,
@@ -1967,6 +1968,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
*/
+ @android.view.RemotableViewMethod
public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end,
int bottom) {
resetResolvedDrawables();
@@ -2042,6 +2044,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @attr ref android.R.styleable#TextView_drawablePadding
*/
+ @android.view.RemotableViewMethod
public void setCompoundDrawablePadding(int pad) {
Drawables dr = mDrawables;
if (pad == 0) {
@@ -7493,7 +7496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
* its selection changed. Only valid while processing the touch gesture
- * of interest.
+ * of interest, in an editable text view.
*/
public boolean didTouchFocusSelect() {
return mEditor != null && getEditor().mTouchFocusSelected;
@@ -11755,7 +11758,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hardwareCanvas.onPostDraw();
blockDisplayList.end();
if (USE_DISPLAY_LIST_PROPERTIES) {
- blockDisplayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
+ blockDisplayList.setLeftTopRightBottom(0, 0, width, height);
}
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 7eff1aa..bc88b62 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -532,21 +532,28 @@ public class TimePicker extends FrameLayout {
private void setContentDescriptions() {
// Minute
- String text = mContext.getString(R.string.time_picker_increment_minute_button);
- mMinuteSpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.time_picker_decrement_minute_button);
- mMinuteSpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mMinuteSpinner, R.id.increment,
+ R.string.time_picker_increment_minute_button);
+ trySetContentDescription(mMinuteSpinner, R.id.decrement,
+ R.string.time_picker_decrement_minute_button);
// Hour
- text = mContext.getString(R.string.time_picker_increment_hour_button);
- mHourSpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.time_picker_decrement_hour_button);
- mHourSpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mHourSpinner, R.id.increment,
+ R.string.time_picker_increment_hour_button);
+ trySetContentDescription(mHourSpinner, R.id.decrement,
+ R.string.time_picker_decrement_hour_button);
// AM/PM
if (mAmPmSpinner != null) {
- text = mContext.getString(R.string.time_picker_increment_set_pm_button);
- mAmPmSpinner.findViewById(R.id.increment).setContentDescription(text);
- text = mContext.getString(R.string.time_picker_decrement_set_am_button);
- mAmPmSpinner.findViewById(R.id.decrement).setContentDescription(text);
+ trySetContentDescription(mAmPmSpinner, R.id.increment,
+ R.string.time_picker_increment_set_pm_button);
+ trySetContentDescription(mAmPmSpinner, R.id.decrement,
+ R.string.time_picker_decrement_set_am_button);
+ }
+ }
+
+ private void trySetContentDescription(View root, int viewId, int contDescResId) {
+ View target = root.findViewById(viewId);
+ if (target != null) {
+ target.setContentDescription(mContext.getString(contDescResId));
}
}
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ef7e651..3d46cdd 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -123,7 +123,7 @@ public class HeavyWeightSwitcherActivity extends Activity {
private OnClickListener mSwitchOldListener = new OnClickListener() {
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0, null);
} catch (RemoteException e) {
}
finish();
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0563846..af722a8 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -33,14 +33,17 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.LayoutInflater;
+import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
+import android.widget.ListView;
import android.widget.TextView;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -122,6 +125,11 @@ public class ResolverActivity extends AlertActivity implements
}
setupAlert();
+
+ ListView lv = mAlert.getListView();
+ if (lv != null) {
+ lv.setOnItemLongClickListener(new ItemLongClickListener());
+ }
}
@Override
@@ -489,5 +497,18 @@ public class ResolverActivity extends AlertActivity implements
mClearDefaultHint.setVisibility(View.GONE);
}
}
+
+ class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ ResolveInfo ri = mAdapter.resolveInfoForPosition(position);
+ Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS")
+ .setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
+ startActivity(in);
+ return true;
+ }
+
+ }
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index e7c3948..eed3e67 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -166,10 +166,13 @@ public class LocalTransport extends IBackupTransport.Stub {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
File packageDir = new File(mDataDir, packageInfo.packageName);
- for (File f : packageDir.listFiles()) {
- f.delete();
+ final File[] fileset = packageDir.listFiles();
+ if (fileset != null) {
+ for (File f : fileset) {
+ f.delete();
+ }
+ packageDir.delete();
}
- packageDir.delete();
return BackupConstants.TRANSPORT_OK;
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index da189f1..07496a7 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -44,10 +44,14 @@ import java.util.Vector;
* <p>When a state machine is created <code>addState</code> is used to build the
* hierarchy and <code>setInitialState</code> is used to identify which of these
* is the initial state. After construction the programmer calls <code>start</code>
- * which initializes the state machine and calls <code>enter</code> for all of the initial
- * state's hierarchy, starting at its eldest parent. For example given the simple
- * state machine below after start is called mP1.enter will have been called and
- * then mS1.enter.</p>
+ * which initializes and starts the state machine. The first action the StateMachine
+ * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
+ * starting at its eldest parent. The calls to enter will be done in the context
+ * of the StateMachines Handler not in the context of the call to start and they
+ * will be invoked before any messages are processed. For example, given the simple
+ * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
+ * messages sent to the state machine will be processed by the current state,
+ * in our simple state machine below that would initially be mS1.processMessage.</p>
<code>
mP1
/ \
@@ -621,8 +625,8 @@ public class StateMachine {
/** The debug flag */
private boolean mDbg = false;
- /** The quit object */
- private static final Object mQuitObj = new Object();
+ /** The SmHandler object, identifies that message is internal */
+ private static final Object mSmHandlerObj = new Object();
/** The current message */
private Message mMsg;
@@ -726,19 +730,18 @@ public class StateMachine {
/** Save the current message */
mMsg = msg;
- /**
- * Check that construction was completed
- */
- if (!mIsConstructionCompleted) {
- Log.e(TAG, "The start method not called, ignore msg: " + msg);
- return;
+ if (mIsConstructionCompleted) {
+ /** Normal path */
+ processMsg(msg);
+ } else if (!mIsConstructionCompleted &&
+ (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
+ /** Initial one time path. */
+ mIsConstructionCompleted = true;
+ invokeEnterMethods(0);
+ } else {
+ throw new RuntimeException("StateMachine.handleMessage: " +
+ "The start method not called, received msg: " + msg);
}
-
- /**
- * Process the message abiding by the hierarchical semantics
- * and perform any requested transitions.
- */
- processMsg(msg);
performTransitions();
if (mDbg) Log.d(TAG, "handleMessage: X");
@@ -852,18 +855,8 @@ public class StateMachine {
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
- /**
- * Construction is complete call all enter methods
- * starting at the first entry.
- */
- mIsConstructionCompleted = true;
- mMsg = obtainMessage(SM_INIT_CMD);
- invokeEnterMethods(0);
-
- /**
- * Perform any transitions requested by the enter methods
- */
- performTransitions();
+ /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
+ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) Log.d(TAG, "completeConstruction: X");
}
@@ -1103,14 +1096,14 @@ public class StateMachine {
/** @see StateMachine#setInitialState(State) */
private final void setInitialState(State initialState) {
- if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
+ if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
mDestState = (State) destState;
- if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + mDestState.getName());
+ if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}
/** @see StateMachine#deferMessage(Message) */
@@ -1127,12 +1120,12 @@ public class StateMachine {
/** @see StateMachine#deferMessage(Message) */
private final void quit() {
if (mDbg) Log.d(TAG, "quit:");
- sendMessage(obtainMessage(SM_QUIT_CMD, mQuitObj));
+ sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
}
/** @see StateMachine#isQuit(Message) */
private final boolean isQuit(Message msg) {
- return (msg.what == SM_QUIT_CMD) && (msg.obj == mQuitObj);
+ return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
}
/** @see StateMachine#isDbg() */
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index acc3c1c..5a7d519 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -105,6 +105,12 @@ public class LockPatternUtils {
*/
public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
+ /**
+ * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should
+ * be used
+ */
+ public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
+
private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
@@ -878,6 +884,28 @@ public class LockPatternUtils {
}
/**
+ * Set whether biometric weak liveliness is enabled.
+ */
+ public void setBiometricWeakLivelinessEnabled(boolean enabled) {
+ long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
+ long newFlag;
+ if (enabled) {
+ newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS;
+ } else {
+ newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS;
+ }
+ setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag);
+ }
+
+ /**
+ * @return Whether the biometric weak liveliness is enabled.
+ */
+ public boolean isBiometricWeakLivelinessEnabled() {
+ long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
+ return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0);
+ }
+
+ /**
* Set whether the lock pattern is enabled.
*/
public void setLockPatternEnabled(boolean enabled) {
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index b04f890..7c937ed 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -20,6 +20,8 @@ package com.google.android.mms.pdu;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
import com.google.android.mms.util.PduCache;
import com.google.android.mms.util.PduCacheEntry;
import com.google.android.mms.util.SqliteWrapper;
@@ -30,7 +32,11 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
+import android.drm.DrmManagerClient;
import android.net.Uri;
+import android.os.FileUtils;
+import android.provider.MediaStore;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.MmsSms;
@@ -42,6 +48,7 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -271,10 +278,12 @@ public class PduPersister {
private final Context mContext;
private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
private PduPersister(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
}
/** Get(or create if not exist) an instance of PduPersister */
@@ -761,6 +770,9 @@ public class PduPersister {
throws MmsException {
OutputStream os = null;
InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
try {
byte[] data = part.getData();
@@ -773,9 +785,38 @@ public class PduPersister {
throw new MmsException("unable to update " + uri.toString());
}
} else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try {
+ path = convertUriToPath(mContext, uri);
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm uri: " + uri + " path: " + path);
+ }
+ File f = new File(path);
+ long len = f.length();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm path: " + path + " len: " + len);
+ }
+ if (len > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
os = mContentResolver.openOutputStream(uri);
if (data == null) {
- Uri dataUri = part.getDataUri();
+ dataUri = part.getDataUri();
if ((dataUri == null) || (dataUri == uri)) {
Log.w(TAG, "Can't find data for this part.");
return;
@@ -788,13 +829,32 @@ public class PduPersister {
byte[] buffer = new byte[8192];
for (int len = 0; (len = is.read(buffer)) != -1; ) {
- os.write(buffer, 0, len);
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
} else {
if (LOCAL_LOGV) {
Log.v(TAG, "Saving data to: " + uri);
}
- os.write(data);
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
}
} catch (FileNotFoundException e) {
@@ -818,7 +878,65 @@ public class PduPersister {
Log.e(TAG, "IOException while closing: " + is, e);
} // Ignore
}
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ /**
+ * This method expects uri in the following format
+ * content://media/<table_name>/<row_index> (or)
+ * file://sdcard/test.mp4
+ * http://test.com/test.mp4
+ *
+ * Here <table_name> shall be "video" or "audio" or "images"
+ * <row_index> the index of the content in given table
+ */
+ static public String convertUriToPath(Context context, Uri uri) {
+ String path = null;
+ if (null != uri) {
+ String scheme = uri.getScheme();
+ if (null == scheme || scheme.equals("") ||
+ scheme.equals(ContentResolver.SCHEME_FILE)) {
+ path = uri.getPath();
+
+ } else if (scheme.equals("http")) {
+ path = uri.toString();
+
+ } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null,
+ null, null);
+ if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Given Uri could not be found" +
+ " in media store");
+ }
+ int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
+ path = cursor.getString(pathIndex);
+ } catch (SQLiteException e) {
+ throw new IllegalArgumentException("Given Uri is not formatted in a way " +
+ "so that it can be found in media store.");
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Given Uri scheme is not supported");
+ }
}
+ return path;
}
private void updateAddress(
diff --git a/core/java/com/google/android/mms/util/DownloadDrmHelper.java b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 0000000..6852eca
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/core/java/com/google/android/mms/util/DrmConvertSession.java b/core/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 0000000..2d8f274
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+import android.provider.Downloads;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}