diff options
Diffstat (limited to 'core')
210 files changed, 4431 insertions, 2948 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; + } +} diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 8e5db36..baf296d 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -21,12 +21,16 @@ #include "jni.h" #include "GraphicsJNI.h" #include <nativehelper/JNIHelp.h> + #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_graphics_SurfaceTexture.h> -#include <cutils/properties.h> +#include <gui/SurfaceTexture.h> + #include <androidfw/ResourceTypes.h> -#include <gui/SurfaceTexture.h> +#include <private/hwui/DrawGlInfo.h> + +#include <cutils/properties.h> #include <SkBitmap.h> #include <SkCanvas.h> @@ -196,7 +200,7 @@ static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) // Functor // ---------------------------------------------------------------------------- -static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz, +static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, Functor *functor) { android::uirenderer::Rect dirty; return renderer->callDrawGLFunction(functor, dirty); @@ -682,16 +686,16 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, DisplayList::destroyDisplayListDeferred(displayList); } -static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, +static jint android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, jint width, jint height, jobject dirty, jint flags) { android::uirenderer::Rect bounds; - bool redraw = renderer->drawDisplayList(displayList, width, height, bounds, flags); - if (redraw && dirty != NULL) { + status_t status = renderer->drawDisplayList(displayList, width, height, bounds, flags); + if (status != DrawGlInfo::kStatusDone && dirty != NULL) { env->CallVoidMethod(dirty, gRectClassInfo.set, int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom)); } - return redraw; + return status; } static void android_view_GLES20Canvas_outputDisplayList(JNIEnv* env, @@ -865,7 +869,7 @@ static JNINativeMethod gMethods[] = { { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize }, - { "nCallDrawGLFunction", "(II)Z", + { "nCallDrawGLFunction", "(II)I", (void*) android_view_GLES20Canvas_callDrawGLFunction }, { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save }, @@ -943,7 +947,7 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize }, { "nSetDisplayListName", "(ILjava/lang/String;)V", (void*) android_view_GLES20Canvas_setDisplayListName }, - { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)Z", + { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)I", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index afbcfc2..8d6fab4 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -20,8 +20,8 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> +#include <utils/ZipFileRO.h> #include <ScopedUtfChars.h> -#include <androidfw/ZipFileRO.h> #include <zlib.h> diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d1e3642..5ae12b6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -60,6 +60,9 @@ <protected-broadcast android:name="android.intent.action.REBOOT" /> <protected-broadcast android:name="android.intent.action.DOCK_EVENT" /> <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" /> + <protected-broadcast android:name="android.intent.action.USER_ADDED" /> + <protected-broadcast android:name="android.intent.action.USER_REMOVED" /> + <protected-broadcast android:name="android.intent.action.USER_SWITCHED" /> <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" /> <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" /> @@ -1032,15 +1035,6 @@ android:label="@string/permlab_clearAppCache" android:description="@string/permdesc_clearAppCache" /> - <!-- Allows an application to read the low-level system log files. - Log entries can contain the user's private information, - which is why this permission is 'dangerous'. --> - <permission android:name="android.permission.READ_LOGS" - android:permissionGroup="android.permission-group.PERSONAL_INFO" - android:protectionLevel="dangerous" - android:label="@string/permlab_readLogs" - android:description="@string/permdesc_readLogs" /> - <!-- Allows an application to use any media decoder when decoding for playback @hide --> <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK" @@ -1076,6 +1070,15 @@ android:label="@string/permlab_dump" android:description="@string/permdesc_dump" /> + <!-- Allows an application to read the low-level system log files. + Log entries can contain the user's private information, + which is why this permission is not available to normal apps. --> + <permission android:name="android.permission.READ_LOGS" + android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS" + android:protectionLevel="signature|system|development" + android:label="@string/permlab_readLogs" + android:description="@string/permdesc_readLogs" /> + <!-- Configure an application for debugging. --> <permission android:name="android.permission.SET_DEBUG_APP" android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS" diff --git a/core/res/res/drawable-hdpi/ic_facial_backup.png b/core/res/res/drawable-hdpi/ic_facial_backup.png Binary files differnew file mode 100644 index 0000000..2956109 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_facial_backup.png diff --git a/core/res/res/drawable-hdpi/intro_bg.png b/core/res/res/drawable-hdpi/intro_bg.png Binary files differnew file mode 100644 index 0000000..a758e7d --- /dev/null +++ b/core/res/res/drawable-hdpi/intro_bg.png diff --git a/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 9584649..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index 5c37873..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_dark.png Binary files differdeleted file mode 100644 index b5faf6f..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_light.png Binary files differdeleted file mode 100644 index 041412b..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_dark.png Binary files differdeleted file mode 100644 index 5717bee..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_light.png Binary files differdeleted file mode 100644 index e874330..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index 96a6c8a..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_light.png Binary files differdeleted file mode 100644 index 96a6c8a..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_dark.png Binary files differdeleted file mode 100644 index 4631d85..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_light.png Binary files differdeleted file mode 100644 index 39c7af4..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_dark.png Binary files differdeleted file mode 100644 index 9c23a18..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_light.png Binary files differdeleted file mode 100644 index 9c23a18..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_down_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 159913c..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index cfee4b7..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_dark.png Binary files differdeleted file mode 100644 index e5f0430..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_light.png Binary files differdeleted file mode 100644 index 7e4ec4a..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_dark.png Binary files differdeleted file mode 100644 index b06017e..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_light.png Binary files differdeleted file mode 100644 index a1000f8..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index b3d6706..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_light.png Binary files differdeleted file mode 100644 index b3d6706..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_dark.png Binary files differdeleted file mode 100644 index 9ee35c7..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_light.png Binary files differdeleted file mode 100644 index 4da4fa7..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_dark.png b/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_dark.png Binary files differdeleted file mode 100644 index 358a13f..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_light.png b/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_light.png Binary files differdeleted file mode 100644 index 358a13f..0000000 --- a/core/res/res/drawable-hdpi/numberpicker_up_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_facial_backup.png b/core/res/res/drawable-mdpi/ic_facial_backup.png Binary files differnew file mode 100644 index 0000000..6ed1327 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_facial_backup.png diff --git a/core/res/res/drawable-mdpi/intro_bg.png b/core/res/res/drawable-mdpi/intro_bg.png Binary files differnew file mode 100644 index 0000000..540da31 --- /dev/null +++ b/core/res/res/drawable-mdpi/intro_bg.png diff --git a/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 50f6e98..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index 67434f6..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_dark.png Binary files differdeleted file mode 100644 index 9c2b833..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_light.png Binary files differdeleted file mode 100644 index dcf2fb7..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_dark.png Binary files differdeleted file mode 100644 index b63c510d..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_light.png Binary files differdeleted file mode 100644 index 55312a1..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index 48e300c..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_light.png Binary files differdeleted file mode 100644 index 48e300c..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_dark.png Binary files differdeleted file mode 100644 index 1558d3d..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_light.png Binary files differdeleted file mode 100644 index 6b6e7e1..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_dark.png Binary files differdeleted file mode 100644 index eb16f8d..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_light.png Binary files differdeleted file mode 100644 index eb16f8d..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_down_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 58a3b64..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index 382943b..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_dark.png Binary files differdeleted file mode 100644 index cf856a1..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_light.png Binary files differdeleted file mode 100644 index 6665953..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_dark.png Binary files differdeleted file mode 100644 index d63d797..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_light.png Binary files differdeleted file mode 100644 index 22b6dbd..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index 4bcce98..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_light.png Binary files differdeleted file mode 100644 index 4bcce98..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_dark.png Binary files differdeleted file mode 100644 index 12ba823..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_light.png Binary files differdeleted file mode 100644 index d841f5a..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_dark.png b/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_dark.png Binary files differdeleted file mode 100644 index bc5e3fa..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_light.png b/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_light.png Binary files differdeleted file mode 100644 index bc5e3fa..0000000 --- a/core/res/res/drawable-mdpi/numberpicker_up_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_facial_backup.png b/core/res/res/drawable-xhdpi/ic_facial_backup.png Binary files differnew file mode 100644 index 0000000..942cf23 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_facial_backup.png diff --git a/core/res/res/drawable-xhdpi/intro_bg.png b/core/res/res/drawable-xhdpi/intro_bg.png Binary files differnew file mode 100644 index 0000000..00466c5 --- /dev/null +++ b/core/res/res/drawable-xhdpi/intro_bg.png diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 111f57e..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index d0ef05b..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_dark.png Binary files differdeleted file mode 100644 index ff21941..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_light.png Binary files differdeleted file mode 100644 index 3e9bdda..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_dark.png Binary files differdeleted file mode 100644 index 0462fca..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_light.png Binary files differdeleted file mode 100644 index a488e8e..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index f61b076..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_light.png Binary files differdeleted file mode 100644 index f61b076..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_dark.png Binary files differdeleted file mode 100644 index 211944e..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_light.png Binary files differdeleted file mode 100644 index 12bc11a..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_dark.png Binary files differdeleted file mode 100644 index 635184c..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_light.png Binary files differdeleted file mode 100644 index 635184c..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_down_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_dark.png Binary files differdeleted file mode 100644 index 470e569..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_light.png Binary files differdeleted file mode 100644 index 16df74d..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_dark.png Binary files differdeleted file mode 100644 index edd4c04..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_light.png Binary files differdeleted file mode 100644 index d8f459a..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_disabled_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_dark.png Binary files differdeleted file mode 100644 index 08bf241..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_light.png Binary files differdeleted file mode 100644 index b2c40f1..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_focused_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_dark.png Binary files differdeleted file mode 100644 index f4f7331..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_light.png Binary files differdeleted file mode 100644 index f4f7331..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_longpressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_dark.png Binary files differdeleted file mode 100644 index 83650b1..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_light.png Binary files differdeleted file mode 100644 index 78085d3..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_normal_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_dark.png b/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_dark.png Binary files differdeleted file mode 100644 index b8f6849..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_light.png b/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_light.png Binary files differdeleted file mode 100644 index b8f6849..0000000 --- a/core/res/res/drawable-xhdpi/numberpicker_up_pressed_holo_light.png +++ /dev/null diff --git a/core/res/res/drawable/numberpicker_down_btn_holo_dark.xml b/core/res/res/drawable/numberpicker_down_btn_holo_dark.xml deleted file mode 100644 index dd6332d..0000000 --- a/core/res/res/drawable/numberpicker_down_btn_holo_dark.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="false" - android:drawable="@drawable/numberpicker_down_normal_holo_dark" /> - - <item android:state_pressed="true" - android:state_enabled="true" - android:drawable="@drawable/numberpicker_down_pressed_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="true" - android:drawable="@drawable/numberpicker_down_focused_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="false" - android:drawable="@drawable/numberpicker_down_disabled_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="true" - android:drawable="@drawable/numberpicker_down_disabled_focused_holo_dark" /> - -</selector> diff --git a/core/res/res/drawable/numberpicker_down_btn_holo_light.xml b/core/res/res/drawable/numberpicker_down_btn_holo_light.xml deleted file mode 100644 index 565fd86..0000000 --- a/core/res/res/drawable/numberpicker_down_btn_holo_light.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="false" - android:drawable="@drawable/numberpicker_down_normal_holo_light" /> - - <item android:state_pressed="true" - android:state_enabled="true" - android:drawable="@drawable/numberpicker_down_pressed_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="true" - android:drawable="@drawable/numberpicker_down_focused_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="false" - android:drawable="@drawable/numberpicker_down_disabled_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="true" - android:drawable="@drawable/numberpicker_down_disabled_focused_holo_light" /> - -</selector> diff --git a/core/res/res/drawable/numberpicker_up_btn_holo_dark.xml b/core/res/res/drawable/numberpicker_up_btn_holo_dark.xml deleted file mode 100644 index 7af3ee4..0000000 --- a/core/res/res/drawable/numberpicker_up_btn_holo_dark.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="false" - android:drawable="@drawable/numberpicker_up_normal_holo_dark" /> - - <item android:state_pressed="true" - android:state_enabled="true" - android:drawable="@drawable/numberpicker_up_pressed_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="true" - android:drawable="@drawable/numberpicker_up_focused_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="false" - android:drawable="@drawable/numberpicker_up_disabled_holo_dark" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="true" - android:drawable="@drawable/numberpicker_up_disabled_focused_holo_dark" /> - -</selector> diff --git a/core/res/res/drawable/numberpicker_up_btn_holo_light.xml b/core/res/res/drawable/numberpicker_up_btn_holo_light.xml deleted file mode 100644 index cbcbb07..0000000 --- a/core/res/res/drawable/numberpicker_up_btn_holo_light.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="false" - android:drawable="@drawable/numberpicker_up_normal_holo_light" /> - - <item android:state_pressed="true" - android:state_enabled="true" - android:drawable="@drawable/numberpicker_up_pressed_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="true" - android:state_focused="true" - android:drawable="@drawable/numberpicker_up_focused_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="false" - android:drawable="@drawable/numberpicker_up_focused_holo_light" /> - - <item android:state_pressed="false" - android:state_enabled="false" - android:state_focused="true" - android:drawable="@drawable/numberpicker_up_disabled_focused_holo_light" /> - -</selector> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml index 43d0a2c..2495b67 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml @@ -153,13 +153,32 @@ </LinearLayout> <!-- Area to overlay FaceLock --> - <View android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_width="512dip" android:layout_height="512dip" android:layout_centerInParent="true" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </RelativeLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml index 8c02888..fb62eb6 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml @@ -156,13 +156,32 @@ </LinearLayout> <!-- Area to overlay FaceLock --> - <View android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_width="512dip" android:layout_height="512dip" android:layout_centerInParent="true" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </RelativeLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml index 802ef82..53ecbac 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml @@ -124,13 +124,32 @@ </RelativeLayout> <!-- Area to overlay FaceLock --> - <View android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_width="512dip" android:layout_height="512dip" android:layout_centerInParent="true" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </RelativeLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml index 40f2492..577668f 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml @@ -119,13 +119,32 @@ </RelativeLayout> <!-- Area to overlay FaceLock --> - <View android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_width="512dip" android:layout_height="512dip" android:layout_centerInParent="true" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </RelativeLayout> diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml index 66d7fd7..3bea16c 100644 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout/keyguard_screen_password_landscape.xml @@ -208,7 +208,8 @@ /> <!-- Area to overlay FaceLock --> - <TextView android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_row="0" android:layout_column="2" @@ -217,7 +218,25 @@ android:layout_gravity="fill" android:layout_width="0dip" android:layout_height="0dip" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml index 426b2f7..6b409aa 100644 --- a/core/res/res/layout/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout/keyguard_screen_password_portrait.xml @@ -199,7 +199,8 @@ /> <!-- Area to overlay FaceLock --> - <TextView android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_row="3" android:layout_column="0" @@ -208,7 +209,25 @@ android:layout_gravity="fill" android:layout_width="0dip" android:layout_height="0dip" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index 1038657..7f9aaa0 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -161,7 +161,8 @@ /> <!-- Area to overlay FaceLock --> - <TextView android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_row="0" android:layout_column="1" @@ -171,7 +172,25 @@ android:layout_marginLeft="8dip" android:layout_width="0dip" android:layout_height="0dip" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 336faa1..9a2e024 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -170,7 +170,8 @@ /> <!-- Area to overlay FaceLock --> - <TextView android:id="@+id/faceLockAreaView" + <RelativeLayout + android:id="@+id/faceLockAreaView" android:visibility="invisible" android:layout_row="4" android:layout_column="0" @@ -181,7 +182,25 @@ android:layout_marginBottom="8dip" android:layout_width="0dip" android:layout_height="0dip" - android:background="@color/facelock_color_background" - /> + android:background="@drawable/intro_bg"> + + <View + android:id="@+id/spotlightMask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/facelock_spotlight_mask" + /> + + <ImageView + android:id="@+id/cancel_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_facial_backup" + /> + + </RelativeLayout> </GridLayout> diff --git a/core/res/res/layout/notification_intruder_content.xml b/core/res/res/layout/notification_intruder_content.xml new file mode 100644 index 0000000..7f37032 --- /dev/null +++ b/core/res/res/layout/notification_intruder_content.xml @@ -0,0 +1,74 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="4dp" + > + <ImageView android:id="@+id/icon" + android:layout_width="32dp" + android:layout_height="32dp" + android:scaleType="center" + android:padding="4dp" + /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="40dp" + android:layout_marginLeft="40dp" + android:orientation="vertical" + > + <TextView android:id="@+id/title" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + /> + <TextView android:id="@+id/text" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginTop="-4dp" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + /> + </LinearLayout> + <LinearLayout + android:id="@+id/actions" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + android:orientation="horizontal" + android:visibility="gone" + > + <Button + style="?android:attr/buttonBarButtonStyle" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" + android:id="@+id/action0" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:visibility="gone" + /> + <Button + style="?android:attr/buttonBarButtonStyle" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" + android:id="@+id/action1" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:visibility="gone" + /> + <Button + style="?android:attr/buttonBarButtonStyle" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" + android:id="@+id/action2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:visibility="gone" + /> + </LinearLayout> +</FrameLayout> diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml index 2967696..b7e7ae1 100644 --- a/core/res/res/layout/number_picker.xml +++ b/core/res/res/layout/number_picker.xml @@ -22,19 +22,26 @@ <ImageButton android:id="@+id/increment" android:layout_width="fill_parent" android:layout_height="wrap_content" - style="?android:attr/numberPickerUpButtonStyle" + android:background="@android:drawable/numberpicker_up_btn" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_increment_button" /> - <view class="android.widget.NumberPicker$CustomEditText" + <EditText android:id="@+id/numberpicker_input" android:layout_width="fill_parent" android:layout_height="wrap_content" - style="?android:attr/numberPickerInputTextStyle" /> + android:textAppearance="@style/TextAppearance.Large.Inverse.NumberPickerInputText" + android:gravity="center" + android:singleLine="true" + android:background="@drawable/numberpicker_input" /> <ImageButton android:id="@+id/decrement" android:layout_width="fill_parent" android:layout_height="wrap_content" - style="?android:attr/numberPickerDownButtonStyle" + android:background="@android:drawable/numberpicker_down_btn" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_decrement_button" /> </merge> diff --git a/core/res/res/layout/number_picker_with_selector_wheel.xml b/core/res/res/layout/number_picker_with_selector_wheel.xml new file mode 100644 index 0000000..c8fa0f7 --- /dev/null +++ b/core/res/res/layout/number_picker_with_selector_wheel.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <view class="android.widget.NumberPicker$CustomEditText" + android:textAppearance="?android:attr/textAppearanceMedium" + android:id="@+id/numberpicker_input" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:singleLine="true" + android:background="@null" /> + +</merge> diff --git a/core/res/res/layout/preference_child_holo.xml b/core/res/res/layout/preference_child_holo.xml index 3c6a12e..4167862 100644 --- a/core/res/res/layout/preference_child_holo.xml +++ b/core/res/res/layout/preference_child_holo.xml @@ -60,7 +60,7 @@ android:layout_alignLeft="@android:id/title" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary" - android:maxLines="4" /> + android:maxLines="10" /> </RelativeLayout> diff --git a/core/res/res/layout/preference_holo.xml b/core/res/res/layout/preference_holo.xml index 48f483d..e574219 100644 --- a/core/res/res/layout/preference_holo.xml +++ b/core/res/res/layout/preference_holo.xml @@ -63,7 +63,7 @@ android:layout_alignLeft="@android:id/title" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary" - android:maxLines="4" /> + android:maxLines="10" /> </RelativeLayout> diff --git a/core/res/res/layout/preference_information_holo.xml b/core/res/res/layout/preference_information_holo.xml index e29ed03..5e03cfb 100644 --- a/core/res/res/layout/preference_information_holo.xml +++ b/core/res/res/layout/preference_information_holo.xml @@ -61,7 +61,7 @@ android:layout_alignLeft="@android:id/title" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary" - android:maxLines="2" /> + android:maxLines="10" /> </RelativeLayout> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index fd9eff3..f020f5d 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -335,7 +335,7 @@ <string name="permlab_writeContacts" msgid="644616215860933284">"skryf kontakdata"</string> <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"Laat die program toe om die kontakdata (adresse) wat op jou tablet gestoor is, te verander. Kwaadwillige programme kan dit dalk gebruik om jou kontakdata uit te vee of te verander."</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"Laat die program toe om die kontakdata (adresse) wat op jou foon gestoor is, te verander. Kwaadwillige programme kan dit dalk gebruik om jou kontakdata uit te vee of te verander."</string> - <string name="permlab_readCallLog" msgid="3478133184624102739">"Lees oproeprekord"</string> + <string name="permlab_readCallLog" msgid="3478133184624102739">"lees oproeprekord"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Laat die program toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te lees. Kwaadwillige programme kan dit gebruik om jou data na ander mense te stuur."</string> <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te lees. Kwaadwillige programme kan dit gebruik om jou data na ander mense te stuur."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"skryf oproeprekord"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index f1379f7..0481a67 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -342,9 +342,9 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የጡባዊተኮህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string> <string name="permlab_readProfile" msgid="6824681438529842282">"የመገለጫ ውሂብዎን ያንብቡ"</string> - <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string> + <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"የአርስዎ መገለጫ ውሂብ ላይ ይፃፉ"</string> - <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string> + <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string> <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"የአንተን ማህበራዊ የውይይት ክፍሎች አንብብ"</string> <string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">" ከአንተ ጓደኞች ማህበራዊ ዝማኔዎችን እንዲደርስባቸው እና እንዲያመሳስል ለመተግበሪያውይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች ይህን መዳረሻ ባንተና በጓደኞችህ መካከል በማህበራዊ አውታረመረቦች ያሉ የግል ተግባቦቶችን ለመዳረስ ሊጠቀሙበት ይችላሉ፡፡"</string> <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"ወደ የአንተ ማህበራዊ የውይይት ክፍሎች ጻፍ"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 8125ee3..d2a4cc1 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -916,8 +916,8 @@ <string name="no" msgid="5141531044935541497">"Zrušit"</string> <string name="dialog_alert_title" msgid="2049658708609043103">"Upozornění"</string> <string name="loading" msgid="7933681260296021180">"Načítání..."</string> - <string name="capital_on" msgid="1544682755514494298">"ZAPNUTO"</string> - <string name="capital_off" msgid="6815870386972805832">"VYPNUTO"</string> + <string name="capital_on" msgid="1544682755514494298">"I"</string> + <string name="capital_off" msgid="6815870386972805832">"O"</string> <string name="whichApplication" msgid="4533185947064773386">"Dokončit akci pomocí aplikace"</string> <string name="alwaysUse" msgid="4583018368000610438">"Použít jako výchozí nastavení pro tuto činnost."</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Výchozí nastavení vymažete v části Nastavení systému > Aplikace > Stažené."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 0b14edb..b139e5e 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -507,8 +507,8 @@ <string name="permdesc_writeDictionary" msgid="8185385716255065291">"Tillader, at appen kan skrive nye ord i brugerordbogen."</string> <string name="permlab_sdcardRead" product="nosdcard" msgid="4086221374639183281">"læse USB-lagerets indhold"</string> <string name="permlab_sdcardRead" product="default" msgid="8537875151845139539">"læse indholdet af SD-kortet"</string> - <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Tillader, at app\'en læser indhold på USB-lager."</string> - <string name="permdesc_sdcardRead" product="default" msgid="7947792373570683542">"Tillader, at app\'en læser indholdet af SD-kortet."</string> + <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Tillader, at appen læser indhold på USB-lageret."</string> + <string name="permdesc_sdcardRead" product="default" msgid="7947792373570683542">"Tillader, at appen læser indholdet af SD-kortet."</string> <string name="permlab_sdcardWrite" product="nosdcard" msgid="85430876310764752">"rette/slette i USB-lager"</string> <string name="permlab_sdcardWrite" product="default" msgid="8079403759001777291">"ret/slet indholdet på SD-kortet"</string> <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Lader appen skrive til USB."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 5ef9483..8761ca9 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -336,11 +336,11 @@ <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"Permite que la aplicación modifique la información de contacto (dirección) almacenada en tu tableta. Las aplicaciones maliciosas pueden utilizar este permiso para borrar o modificar tu información de contacto."</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"Permite que la aplicación modifique la información de contacto (dirección) almacenada en tu dispositivo. Las aplicaciones maliciosas pueden utilizar este permiso para borrar o modificar tu información de contacto."</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"leer el registro de llamadas"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Permite que la aplicación lea el registro de llamadas del tablet, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para enviar tus datos a otros usuarios."</string> - <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Permite que la aplicación lea el registro de llamadas del teléfono, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para enviar tus datos a otros usuarios."</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Permite que la aplicación lea el registro de llamadas de la tableta, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para enviar tus datos a otros usuarios."</string> + <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Permite que la aplicación lea el registro de llamadas del dispositivo, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para enviar tus datos a otros usuarios."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"escribir en el registro de llamadas"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que la aplicación modifique el registro de llamadas del tablet, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que la aplicación modifique el registro de llamadas del teléfono, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite que la aplicación modifique el registro de llamadas de la tableta, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite que la aplicación modifique el registro de llamadas del dispositivo, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string> <string name="permlab_readProfile" msgid="6824681438529842282">"Leer tus datos de perfil"</string> <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"Permite que la aplicación lea la información de perfil almacenada en tu dispositivo, como tu nombre e información de contacto. Esto significa que la aplicación puede identificarte y enviar tu información de perfil a otros."</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"Escrib. en datos de tu perfil"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 5a536e6..7ee47e7 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -338,7 +338,7 @@ <string name="permlab_readCallLog" msgid="3478133184624102739">"خواندن گزارش تماس"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"به برنامه اجازه میدهد گزارشات تماس رایانه لوحی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را بخواند. برنامههای مخرب ممکن است از این ویژگی برای ارسال دادههای شما به دیگران استفاده کنند."</string> <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"به برنامه اجازه میدهد گزارشات تماس تلفنی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را بخواند. برنامههای مخرب ممکن است از این ویژگی برای ارسال دادههای شما به دیگران استفاده کنند."</string> - <string name="permlab_writeCallLog" msgid="8552045664743499354">"نوشتن در گزارش تماس"</string> + <string name="permlab_writeCallLog" msgid="8552045664743499354">"نوشتن گزارش تماس"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"به برنامه اجازه میدهد گزارشات تماس رایانه لوحی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"به برنامه اجازه میدهد گزارشات تماس تلفنی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string> <string name="permlab_readProfile" msgid="6824681438529842282">"خواندن دادههای نمایه شما"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 88fe721..d044a70 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -507,7 +507,7 @@ <string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permet à l\'application d\'enregistrer de nouveaux mots dans le dictionnaire personnel de l\'utilisateur."</string> <string name="permlab_sdcardRead" product="nosdcard" msgid="4086221374639183281">"lire contenu de la mémoire USB"</string> <string name="permlab_sdcardRead" product="default" msgid="8537875151845139539">"lire le contenu de la carte SD"</string> - <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Permet à l\'appli de lire contenu mémoire USB."</string> + <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Permet à l\'appli de lire contenu de mémoire USB."</string> <string name="permdesc_sdcardRead" product="default" msgid="7947792373570683542">"Permet à l\'application de lire le contenu de la carte SD."</string> <string name="permlab_sdcardWrite" product="nosdcard" msgid="85430876310764752">"Modifier/Supprimer contenu mémoire USB"</string> <string name="permlab_sdcardWrite" product="default" msgid="8079403759001777291">"modifier/supprimer le contenu de la carte SD"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index d2b4f04..55a298e 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -108,7 +108,7 @@ <string name="fcComplete" msgid="3118848230966886575">"Kode fitur selesai."</string> <string name="fcError" msgid="3327560126588500777">"Masalah sambungan atau kode fitur tidak valid."</string> <string name="httpErrorOk" msgid="1191919378083472204">"OK"</string> - <string name="httpError" msgid="7956392511146698522">"Terjadi galat jaringan."</string> + <string name="httpError" msgid="7956392511146698522">"Terjadi kesalahan jaringan."</string> <string name="httpErrorLookup" msgid="4711687456111963163">"Tidak dapat menemukan URL."</string> <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Skema autentikasi situs tidak didukung."</string> <string name="httpErrorAuth" msgid="1435065629438044534">"Tidak dapat mengautentikasi."</string> @@ -123,7 +123,7 @@ <string name="httpErrorFile" msgid="2170788515052558676">"Tidak dapat mengakses file."</string> <string name="httpErrorFileNotFound" msgid="6203856612042655084">"Tidak dapat menemukan file yang diminta."</string> <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Terlalu banyak permintaan yang diproses. Coba lagi nanti."</string> - <string name="notification_title" msgid="8967710025036163822">"Galat saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string> + <string name="notification_title" msgid="8967710025036163822">"Kesalahan saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string> <string name="contentServiceSync" msgid="8353523060269335667">"Sinkron"</string> <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkron"</string> <string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Terlalu banyak <xliff:g id="CONTENT_TYPE">%s</xliff:g> penghapusan."</string> @@ -396,7 +396,7 @@ <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Mengizinkan apl merusak penyimpanan internal."</string> <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"memasang/melepas penyimpanan internal"</string> <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Mengizinkan apl memasang/melepas penyimpanan internal."</string> - <string name="permlab_asec_rename" msgid="7496633954080472417">"ubah nama penyimpanan internal"</string> + <string name="permlab_asec_rename" msgid="7496633954080472417">"ganti nama penyimpanan internal"</string> <string name="permdesc_asec_rename" msgid="1794757588472127675">"Mengizinkan apl mengganti nama penyimpanan internal."</string> <string name="permlab_vibrate" msgid="7768356019980849603">"mengontrol penggetar"</string> <string name="permdesc_vibrate" msgid="6284989245902300945">"Mengizinkan aplikasi untuk mengendalikan vibrator."</string> @@ -1061,7 +1061,7 @@ <string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string> <string name="ext_media_checking_notification_title" product="nosdcard" msgid="3449816005351468560">"Menyiapkan penyimpanan USB"</string> <string name="ext_media_checking_notification_title" product="default" msgid="5457603418970994050">"Menyiapkan kartu SD"</string> - <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa galat."</string> + <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa kesalahan."</string> <string name="ext_media_nofs_notification_title" product="nosdcard" msgid="7788040745686229307">"Penyimpanan USB kosong"</string> <string name="ext_media_nofs_notification_title" product="default" msgid="780477838241212997">"Kartu SD kosong"</string> <string name="ext_media_nofs_notification_message" product="nosdcard" msgid="7840121067427269500">"Penyimpanan USB kosong atau sistem berkasnya tidak didukung."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 40a4d01..f51f232 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -130,7 +130,7 @@ <string name="low_memory" product="tablet" msgid="6494019234102154896">"שטח האחסון של הטבלט מלא. מחק קבצים כדי לפנות מקום."</string> <string name="low_memory" product="default" msgid="3475999286680000541">"שטח האחסון של הטלפון מלא. מחק חלק מהקבצים כדי לפנות שטח."</string> <string name="me" msgid="6545696007631404292">"אני"</string> - <string name="power_dialog" product="tablet" msgid="8545351420865202853">"אפשרויות טבלט"</string> + <string name="power_dialog" product="tablet" msgid="8545351420865202853">"אפשרויות טאבלט"</string> <string name="power_dialog" product="default" msgid="1319919075463988638">"אפשרויות טלפון"</string> <string name="silent_mode" msgid="7167703389802618663">"מצב שקט"</string> <string name="turn_on_radio" msgid="3912793092339962371">"הפעל חיבור אלחוטי"</string> @@ -146,7 +146,7 @@ <string name="shutdown_confirm_question" msgid="2906544768881136183">"האם ברצונך לבצע כיבוי?"</string> <string name="recent_tasks_title" msgid="3691764623638127888">"נוצרו לאחרונה"</string> <string name="no_recent_tasks" msgid="8794906658732193473">"אין יישומים אחרונים"</string> - <string name="global_actions" product="tablet" msgid="408477140088053665">"אפשרויות טבלט"</string> + <string name="global_actions" product="tablet" msgid="408477140088053665">"אפשרויות טאבלט"</string> <string name="global_actions" product="default" msgid="2406416831541615258">"אפשרויות טלפון"</string> <string name="global_action_lock" msgid="2844945191792119712">"נעילת מסך"</string> <string name="global_action_power_off" msgid="4471879440839879722">"כיבוי"</string> @@ -376,7 +376,7 @@ <string name="permdesc_recordAudio" msgid="2387462233976248635">"מאפשר ליישום לגשת לנתיב להקלטת אודיו."</string> <string name="permlab_camera" msgid="3616391919559751192">"צלם תמונות וסרטונים"</string> <string name="permdesc_camera" msgid="1507407407002492176">"מאפשר ליישום לצלם תמונות וסרטוני וידאו עם המצלמה. הגדרה זו מאפשרת ליישום לאסוף תמונות שהמצלמה קולטת בכל עת."</string> - <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"השבת טבלט לצמיתות"</string> + <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"השבת טאבלט לצמיתות"</string> <string name="permlab_brick" product="default" msgid="8337817093326370537">"השבת טלפון לצמיתות"</string> <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"מאפשר ליישום להשבית את הטבלט כולו לצמיתות. זו הרשאה מסוכנת מאוד."</string> <string name="permdesc_brick" product="default" msgid="5788903297627283099">"מאפשר ליישום להשבית את הטלפון כולו לצמיתות. זו הרשאה מסוכנת מאוד."</string> @@ -412,7 +412,7 @@ <string name="permdesc_callPhone" msgid="6396463004110544744">"מאפשר ליישום להתקשר למספרי טלפון ללא התערבותך. יישומים זדוניים עלולים לגרום לחיובי שיחות בלתי צפויים בחשבון הטלפון שלך. לתשומת לבך, הרשאה זו לא מאפשרת ליישום להתקשר למספרי חירום."</string> <string name="permlab_callPrivileged" msgid="4198349211108497879">"התקשר ישירות לכל מספר טלפון"</string> <string name="permdesc_callPrivileged" msgid="1689024901509996810">"מאפשר ליישום להתקשר לכל מספר טלפון שהוא, כולל מספרי חירום, ללא התערבותך. יישומים זדוניים עלולים לבצע שיחות מיותרות ולא חוקיות לשירותי חירום."</string> - <string name="permlab_performCdmaProvisioning" product="tablet" msgid="4842576994144604821">"הפעל ישירות התקנת טבלט מסוג CDMA"</string> + <string name="permlab_performCdmaProvisioning" product="tablet" msgid="4842576994144604821">"הפעל ישירות התקנת טאבלט מסוג CDMA"</string> <string name="permlab_performCdmaProvisioning" product="default" msgid="5604848095315421425">"הפעל ישירות הגדרה של טלפון CDMA"</string> <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"מאפשר ליישום להפעיל הקצאת CDMA. יישומים זדוניים עלולים להפעיל הקצאת CDMA ללא צורך."</string> <string name="permlab_locationUpdates" msgid="7785408253364335740">"שלוט בהתראות על עדכון מיקום"</string> @@ -429,7 +429,7 @@ <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"מניעת מעבר הטלפון למצב שינה"</string> <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"מאפשר ליישום למנוע מהטבלט לעבור למצב שינה."</string> <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"מאפשר ליישום למנוע מהטלפון לעבור למצב שינה."</string> - <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"הפעלה או כיבוי של טבלט"</string> + <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"הפעלה או כיבוי של טאבלט"</string> <string name="permlab_devicePower" product="default" msgid="4928622470980943206">"הפעל או כבה את הטלפון"</string> <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"מאפשר ליישום להפעיל או לכבות את הטבלט."</string> <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"מאפשר ליישום להפעיל או לכבות את הטלפון."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 87f8f8d..363832d 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -336,11 +336,11 @@ <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"Membenarkan apl untuk mengubah suai data kenalan (alamat) yang disimpan pada tablet anda. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai data kenalan anda."</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"Membenarkan apl untuk mengubah suai data kenalan (alamat) yang disimpan pada telefon anda. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai data kenalan anda."</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"baca log panggilan"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Membenarkan apl membaca log panggilan tablet anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakan ini untuk menghantar data anda kepada orang lain."</string> - <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Membenarkan apl membaca log panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakan ini untuk menghantar data anda kepada orang lain."</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Membenarkan apl membaca log panggilan tablet anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk menghantar data anda kepada orang lain."</string> + <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Membenarkan apl membaca log panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk menghantar data anda kepada orang lain."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"tulis log panggilan"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Membenarkan apl untuk mengubah suai panggilan tablet anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakan ini untuk memadam atau mengubah suai log panggilan anda."</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Membenarkan apl untuk mengubah suai panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakan ini untuk memadam atau mengubah suai log panggilan anda."</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Membenarkan apl untuk mengubah suai panggilan tablet anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai log panggilan anda."</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Membenarkan apl untuk mengubah suai panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai log panggilan anda."</string> <string name="permlab_readProfile" msgid="6824681438529842282">"baca data profil anda"</string> <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"Membenarkan apl untuk membaca maklumat profil peribadi yang disimpan dalam peranti anda, seperti nama dan maklumat kenalan anda. Ini bermakna apl lain boleh mengenal pasti anda dan menghantar maklumat profil anda kepada orang lain."</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"tulis ke data profil anda"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index fec439d..384fdac 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -507,7 +507,7 @@ <string name="permdesc_writeDictionary" msgid="8185385716255065291">"Lar appen skrive nye ord i brukerordlisten."</string> <string name="permlab_sdcardRead" product="nosdcard" msgid="4086221374639183281">"lese innhold på USB-lagr."</string> <string name="permlab_sdcardRead" product="default" msgid="8537875151845139539">"lese innhold på SD-kortet"</string> - <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Lar appen lese innhold på USB-lagringsenheten."</string> + <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"Lar appen lese innhold på USB-lager."</string> <string name="permdesc_sdcardRead" product="default" msgid="7947792373570683542">"Lar appen lese innhold på SD-kortet."</string> <string name="permlab_sdcardWrite" product="nosdcard" msgid="85430876310764752">"endre/slette innh. i USB-lagr."</string> <string name="permlab_sdcardWrite" product="default" msgid="8079403759001777291">"redigere/slette innhold på minnekort"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index eee0d80..6559fc5 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -743,8 +743,7 @@ <string name="double_tap_toast" msgid="4595046515400268881">"Tips! Dubbelknacka om du vill zooma in eller ut."</string> <string name="autofill_this_form" msgid="4616758841157816676">"Autofyll"</string> <string name="setup_autofill" msgid="7103495070180590814">"Ange Autofyll"</string> - <!-- no translation found for autofill_address_name_separator (6350145154779706772) --> - <skip /> + <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string> <string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <string name="autofill_address_summary_format" msgid="4874459455786827344">"$1$2$3"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 3b870cf..39d2aba 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -335,12 +335,12 @@ <string name="permlab_writeContacts" msgid="644616215860933284">"andika data ya anwani"</string> <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"Inaruhusu programu kurekebisha data ya mwasiliani(anwani) iliyohifadhiwa kwenye kompyuta yako ki. programu hasidi zinaweza tumia hii kufuta au kurekebisha data yako ya mwasiliani."</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"Inaruhusu programu kurekebisha data ya mwasiliani(anwani) iliyohifadhiwa kwenye simu yako. Programu hasidi zinaweza kutumia hii kufuta au kurekebisha data yako ya mwasiliani."</string> - <string name="permlab_readCallLog" msgid="3478133184624102739">"soma kumbukumbu ya simu"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Huruhusu programu kusoma kumbukumbu ya kompyuta kibao yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kutuma data yako kwa watu wengine."</string> - <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Huruhusu programu kusoma kumbukumbu ya simu yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kutuma data yako kwa watu wengine."</string> - <string name="permlab_writeCallLog" msgid="8552045664743499354">"andika kumbukumbu ya simu"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Huruhusu programu kusoma kumbukumbu ya kompyuta kibao yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha kumbukumbu ya simu yako."</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Huruhusu programu kusoma kumbukumbu ya simu yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha kumbukumbu ya simu yako."</string> + <string name="permlab_readCallLog" msgid="3478133184624102739">"soma rajisi ya simu"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"Huruhusu programu kusoma rajisi ya simu ya kompyuta kibao yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kutuma data yako kwa watu wengine."</string> + <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"Huruhusu programu kusoma rajisi ya simu yako, ikiwa ni pamoja na data kuhusu simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kutuma data yako kwa watu wengine."</string> + <string name="permlab_writeCallLog" msgid="8552045664743499354">"andika rajisi ya simu"</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Huruhusu programu kurekebisha rajisi ya kompyuta kibao yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Huruhusu programu kurekebisha rajisi ya simu yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string> <string name="permlab_readProfile" msgid="6824681438529842282">"soma data ya maelezo yako mafupi"</string> <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"Inaruhusu programu kusoma maelezo mafupi ya kibinafsi yaliyohifadhiwa kwenye kifaa chako, kama vile jina lako na taarifa ya kuwasiliana. Hii ina maanisha programu inaweza kukutambua na kutuma taarifa yako fupi ya kibinafsi kwa wengine."</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"andika kwenye data ya maelezo yako mafupi"</string> diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml index 7fa7658..49c8893 100644 --- a/core/res/res/values-sw600dp/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -21,7 +21,7 @@ for different hardware and product builds. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- see comment in values/config.xml --> - <integer name="config_longPressOnPowerBehavior">2</integer> + <integer name="config_longPressOnPowerBehavior">1</integer> <!-- Enable lockscreen rotation --> <bool name="config_enableLockScreenRotation">true</bool> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 0542f8e..4b59923 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -336,11 +336,11 @@ <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"อนุญาตให้แอปพลิเคชันแก้ไขข้อมูลการติดต่อ (ที่อยู่) ที่เก็บไว้ในแท็บเล็ตของคุณ แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ลบหรือแก้ไขข้อมูลการติดต่อของคุณ"</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"อนุญาตให้แอปพลิเคชันแก้ไขข้อมูลการติดต่อ (ที่อยู่) ที่เก็บไว้ในโทรศัพท์ของคุณ แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ลบหรือแก้ไขข้อมูลการติดต่อของคุณ"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"อ่านประวัติการโทร"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"อนุญาตให้แอปพลิเคชันอ่านประวัติการโทรของแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปพลิเคชันที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อส่งข้อมูลของคุณให้กับบุคคลอื่น"</string> - <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"อนุญาตให้แอปพลิเคชันอ่านประวัติการโทรของโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปพลิเคชันที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อส่งข้อมูลของคุณให้กับบุคคลอื่น"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"อนุญาตให้แอปอ่านประวัติการโทรจากแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อส่งข้อมูลของคุณให้กับบุคคลอื่น"</string> + <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"อนุญาตให้แอปอ่านประวัติการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อส่งข้อมูลของคุณให้กับบุคคลอื่น"</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"เขียนประวัติการโทร"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"อนุญาตให้แอปพลิเคชันแก้ไขประวัติการโทรของแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปพลิเคชันที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"อนุญาตให้แอปพลิเคชันแก้ไขประวัติการโทรของโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปพลิเคชันที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"อนุญาตให้แอปแก้ไขประวัติการโทรจากแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"อนุญาตให้แอปแก้ไขประวัติการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string> <string name="permlab_readProfile" msgid="6824681438529842282">"อ่านข้อมูลโปรไฟล์ของคุณ"</string> <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"อนุญาตให้แอปพลิเคชันอ่านข้อมูลโปรไฟล์ส่วนบุคคลที่เก็บไว้บนอุปกรณ์ของคุณ เช่น ชื่อและข้อมูลติดต่อ ซึ่งหมายความว่าแอปพลิเคชันจะสามารถระบุตัวตนของคุณและส่งข้อมูลโปรไฟล์ของคุณแก่ผู้อื่นได้"</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"เขียนลงในข้อมูลโปรไฟล์ของคุณ"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 81d0ec6..d7a1e43 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -145,7 +145,7 @@ <string name="shutdown_confirm" product="default" msgid="649792175242821353">"您的手机会关机。"</string> <string name="shutdown_confirm_question" msgid="2906544768881136183">"您要关机吗?"</string> <string name="recent_tasks_title" msgid="3691764623638127888">"近期任务"</string> - <string name="no_recent_tasks" msgid="8794906658732193473">"最近没有运行任何应用程序"</string> + <string name="no_recent_tasks" msgid="8794906658732193473">"最近没有运行任何应用"</string> <string name="global_actions" product="tablet" msgid="408477140088053665">"平板电脑选项"</string> <string name="global_actions" product="default" msgid="2406416831541615258">"手机选项"</string> <string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string> @@ -179,162 +179,162 @@ <string name="permgrouplab_systemTools" msgid="4652191644082714048">"系统工具"</string> <string name="permgroupdesc_systemTools" msgid="8162102602190734305">"对系统进行低级访问和控制。"</string> <string name="permgrouplab_developmentTools" msgid="3446164584710596513">"开发工具"</string> - <string name="permgroupdesc_developmentTools" msgid="7058828032358142018">"只有应用程序开发人员才需要的功能。"</string> + <string name="permgroupdesc_developmentTools" msgid="7058828032358142018">"只有应用开发人员才需要的功能。"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"存储"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"访问 USB 存储设备。"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"访问 SD 卡。"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string> - <string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用程序停用状态栏或者增删系统图标。"</string> + <string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"状态栏"</string> - <string name="permdesc_statusBarService" msgid="716113660795976060">"允许以状态栏形式显示应用程序。"</string> + <string name="permdesc_statusBarService" msgid="716113660795976060">"允许以状态栏形式显示应用。"</string> <string name="permlab_expandStatusBar" msgid="1148198785937489264">"展开/收拢状态栏"</string> - <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允许应用程序展开或折叠状态栏。"</string> + <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允许应用展开或折叠状态栏。"</string> <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"拦截外拨电话"</string> - <string name="permdesc_processOutgoingCalls" msgid="1152111671618301044">"允许应用程序处理外拨通话并更改要拨打的号码。恶意应用程序可能会监视、重定向或阻止外拨通话。"</string> + <string name="permdesc_processOutgoingCalls" msgid="1152111671618301044">"允许应用处理外拨通话并更改要拨打的号码。恶意应用可能会监视、重定向或阻止外拨通话。"</string> <string name="permlab_receiveSms" msgid="2697628268086208535">"接收短信"</string> - <string name="permdesc_receiveSms" msgid="8107887121893611793">"允许应用程序接收和处理短信。恶意应用程序可能会监视您的短信,或删除短信而不向您显示。"</string> + <string name="permdesc_receiveSms" msgid="8107887121893611793">"允许应用接收和处理短信。恶意应用可能会监视您的短信,或删除短信而不向您显示。"</string> <string name="permlab_receiveMms" msgid="8894700916188083287">"接收彩信"</string> - <string name="permdesc_receiveMms" msgid="1424805308566612086">"允许应用程序接收和处理彩信。恶意应用程序可能会监视您的短信,或删除短信而不向您显示。"</string> + <string name="permdesc_receiveMms" msgid="1424805308566612086">"允许应用接收和处理彩信。恶意应用可能会监视您的短信,或删除短信而不向您显示。"</string> <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"接收紧急广播"</string> - <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"允许应用程序接收和处理紧急广播消息。此权限仅适用于系统应用程序。"</string> + <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"允许应用接收和处理紧急广播消息。此权限仅适用于系统应用。"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"发送短信"</string> - <string name="permdesc_sendSms" msgid="906546667507626156">"允许应用程序发送短信。恶意应用程序可能会未经您的确认而发送短信,由此产生相关费用。"</string> + <string name="permdesc_sendSms" msgid="906546667507626156">"允许应用发送短信。恶意应用可能会未经您的确认而发送短信,由此产生相关费用。"</string> <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"不经确认直接发送短信"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="3437759207020400204">"允许应用程序发送短信。恶意应用程序可能会未经您的确认而发送短信,由此产生相关费用。"</string> + <string name="permdesc_sendSmsNoConfirmation" msgid="3437759207020400204">"允许应用发送短信。恶意应用可能会未经您的确认而发送短信,由此产生相关费用。"</string> <string name="permlab_readSms" msgid="4085333708122372256">"读取短信或彩信"</string> - <string name="permdesc_readSms" product="tablet" msgid="2341692916884515613">"允许应用程序读取您的平板电脑或 SIM 卡上存储的短信。恶意应用程序可能会读取您的机密短信。"</string> - <string name="permdesc_readSms" product="default" msgid="5653850482025875493">"允许应用程序读取您的手机或 SIM 卡上存储的短信。恶意应用程序可能借此读取您的机密短信。"</string> + <string name="permdesc_readSms" product="tablet" msgid="2341692916884515613">"允许应用读取您的平板电脑或 SIM 卡上存储的短信。恶意应用可能会读取您的机密短信。"</string> + <string name="permdesc_readSms" product="default" msgid="5653850482025875493">"允许应用读取您的手机或 SIM 卡上存储的短信。恶意应用可能借此读取您的机密短信。"</string> <string name="permlab_writeSms" msgid="6881122575154940744">"编辑短信或彩信"</string> - <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"允许应用程序对平板电脑或 SIM 卡上存储的短信执行写入操作。恶意应用程序可能会删除您的短信。"</string> - <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"允许应用程序对手机或 SIM 卡上存储的短信执行写入操作。恶意应用程序可能会删除您的短信。"</string> + <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"允许应用对平板电脑或 SIM 卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string> + <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"允许应用对手机或 SIM 卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string> <string name="permlab_receiveWapPush" msgid="8258226427716551388">"接收 WAP"</string> - <string name="permdesc_receiveWapPush" msgid="7983455145335316872">"允许应用程序接收和处理 WAP 短信。恶意应用程序可能会监视您的短信,或删除短信而不向您显示。"</string> - <string name="permlab_getTasks" msgid="6466095396623933906">"检索正在运行的应用程序"</string> - <string name="permdesc_getTasks" msgid="6608159250520381359">"允许应用程序针对近期运行的和当前正在运行的任务检索相关信息。恶意应用程序可能会发现有关其他应用程序的私密信息。"</string> - <string name="permlab_reorderTasks" msgid="2018575526934422779">"对正在运行的应用程序重新排序"</string> - <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允许应用程序将任务移动到前台和后台。恶意应用程序可能会不受您的控制,强行让自己处于前台。"</string> - <string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用程序"</string> - <string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用程序删除任务并终止这些任务的应用程序。恶意应用程序可以籍此影响其他应用程序的行为。"</string> + <string name="permdesc_receiveWapPush" msgid="7983455145335316872">"允许应用接收和处理 WAP 短信。恶意应用可能会监视您的短信,或删除短信而不向您显示。"</string> + <string name="permlab_getTasks" msgid="6466095396623933906">"检索正在运行的应用"</string> + <string name="permdesc_getTasks" msgid="6608159250520381359">"允许应用针对近期运行的和当前正在运行的任务检索相关信息。恶意应用可能会发现有关其他应用的私密信息。"</string> + <string name="permlab_reorderTasks" msgid="2018575526934422779">"对正在运行的应用重新排序"</string> + <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允许应用将任务移动到前台和后台。恶意应用可能会不受您的控制,强行让自己处于前台。"</string> + <string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用"</string> + <string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用删除任务并终止这些任务的应用。恶意应用可以籍此影响其他应用的行为。"</string> <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"设置屏幕兼容性"</string> - <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许该应用程序控制其他应用程序的屏幕兼容模式。恶意应用程序可以籍此影响其他应用程序的行为。"</string> - <string name="permlab_setDebugApp" msgid="3022107198686584052">"启用应用程序调试"</string> - <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允许该应用程序对其他应用程序启用调试。恶意应用程序可以籍此终止其他的应用程序。"</string> + <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许该应用控制其他应用的屏幕兼容模式。恶意应用可以籍此影响其他应用的行为。"</string> + <string name="permlab_setDebugApp" msgid="3022107198686584052">"启用应用调试"</string> + <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允许该应用对其他应用启用调试。恶意应用可以籍此终止其他的应用。"</string> <string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string> - <string name="permdesc_changeConfiguration" msgid="4372223873154296076">"允许该应用程序更改当前配置,例如语言区域或整体的字体大小。"</string> + <string name="permdesc_changeConfiguration" msgid="4372223873154296076">"允许该应用更改当前配置,例如语言区域或整体的字体大小。"</string> <string name="permlab_enableCarMode" msgid="5684504058192921098">"启用车载模式"</string> - <string name="permdesc_enableCarMode" msgid="4853187425751419467">"允许应用程序启用车载模式。"</string> + <string name="permdesc_enableCarMode" msgid="4853187425751419467">"允许应用启用车载模式。"</string> <string name="permlab_killBackgroundProcesses" msgid="8373714752793061963">"结束后台进程"</string> - <string name="permdesc_killBackgroundProcesses" msgid="931129103262126617">"允许应用程序在内存空间充足的情况下终止其他应用程序的后台进程。"</string> - <string name="permlab_forceStopPackages" msgid="2329627428832067700">"强行停止其他应用程序"</string> - <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"允许应用程序强行停止其他应用程序。"</string> - <string name="permlab_forceBack" msgid="652935204072584616">"强制应用程序关闭"</string> - <string name="permdesc_forceBack" msgid="3892295830419513623">"允许应用程序强制关闭任何前台活动并返回。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_killBackgroundProcesses" msgid="931129103262126617">"允许应用在内存空间充足的情况下终止其他应用的后台进程。"</string> + <string name="permlab_forceStopPackages" msgid="2329627428832067700">"强行停止其他应用"</string> + <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"允许应用强行停止其他应用。"</string> + <string name="permlab_forceBack" msgid="652935204072584616">"强制应用关闭"</string> + <string name="permdesc_forceBack" msgid="3892295830419513623">"允许应用强制关闭任何前台活动并返回。普通应用绝不需要此权限。"</string> <string name="permlab_dump" msgid="1681799862438954752">"检索系统内部状态"</string> - <string name="permdesc_dump" msgid="1778299088692290329">"允许应用程序检索系统的内部状态。恶意应用程序可能会检索一般情况下绝不需要检索的多种私人信息和安全信息。"</string> + <string name="permdesc_dump" msgid="1778299088692290329">"允许应用检索系统的内部状态。恶意应用可能会检索一般情况下绝不需要检索的多种私人信息和安全信息。"</string> <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"检索屏幕内容"</string> - <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"允许应用程序检索活动窗口的内容。恶意应用程序可能会检索整个窗口的内容,并检查其中除密码以外的所有文字。"</string> + <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"允许应用检索活动窗口的内容。恶意应用可能会检索整个窗口的内容,并检查其中除密码以外的所有文字。"</string> <string name="permlab_shutdown" msgid="7185747824038909016">"部分关机"</string> <string name="permdesc_shutdown" msgid="7046500838746291775">"使活动管理器进入关闭状态。不执行彻底关机。"</string> - <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"禁止切换应用程序"</string> - <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"阻止用户切换到其他应用程序。"</string> - <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"监控所有应用程序的启动"</string> - <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允许应用程序监视和控制系统是如何启动活动的。恶意应用程序可能会完全破坏系统。此权限只有在进行开发时才需要,正常使用情况下绝不需要。"</string> + <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"禁止切换应用"</string> + <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"阻止用户切换到其他应用。"</string> + <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"监控所有应用的启动"</string> + <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允许应用监视和控制系统是如何启动活动的。恶意应用可能会完全破坏系统。此权限只有在进行开发时才需要,正常使用情况下绝不需要。"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"发送包删除的广播"</string> - <string name="permdesc_broadcastPackageRemoved" msgid="6621901216207931089">"允许应用程序广播一条有关已删除了应用程序包的通知。恶意应用程序可能借此终止其他任何正在运行的应用程序。"</string> + <string name="permdesc_broadcastPackageRemoved" msgid="6621901216207931089">"允许应用广播一条有关已删除了应用包的通知。恶意应用可能借此终止其他任何正在运行的应用。"</string> <string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"发送短信收到的广播"</string> - <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"允许应用程序广播一条有关已收到短信的通知。恶意应用程序可能借此伪造接到的短信。"</string> + <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"允许应用广播一条有关已收到短信的通知。恶意应用可能借此伪造接到的短信。"</string> <string name="permlab_broadcastWapPush" msgid="3145347413028582371">"发送 WAP-PUSH 收到的广播"</string> - <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"允许应用程序广播一条有关已收到 WAP PUSH 短信的通知。恶意应用程序可能借此伪造短信接收,或在后台将任意网页的内容替换为恶意内容。"</string> + <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"允许应用广播一条有关已收到 WAP PUSH 短信的通知。恶意应用可能借此伪造短信接收,或在后台将任意网页的内容替换为恶意内容。"</string> <string name="permlab_setProcessLimit" msgid="2451873664363662666">"限制运行的进程个数"</string> - <string name="permdesc_setProcessLimit" msgid="7318061314040879542">"允许应用程序控制将运行的进程数上限。普通应用程序绝不需要此权限。"</string> - <string name="permlab_setAlwaysFinish" msgid="238828158465736054">"关闭所有后台应用程序"</string> - <string name="permdesc_setAlwaysFinish" msgid="7471310652868841499">"允许应用程序控制活动在转入后台后是否立即结束。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_setProcessLimit" msgid="7318061314040879542">"允许应用控制将运行的进程数上限。普通应用绝不需要此权限。"</string> + <string name="permlab_setAlwaysFinish" msgid="238828158465736054">"关闭所有后台应用"</string> + <string name="permdesc_setAlwaysFinish" msgid="7471310652868841499">"允许应用控制活动在转入后台后是否立即结束。普通应用绝不需要此权限。"</string> <string name="permlab_batteryStats" msgid="7863923071360031652">"修改电池统计信息"</string> - <string name="permdesc_batteryStats" msgid="6835186932305744068">"允许应用程序修改收集到的电池统计信息。普通应用程序不能使用此权限。"</string> + <string name="permdesc_batteryStats" msgid="6835186932305744068">"允许应用修改收集到的电池统计信息。普通应用不能使用此权限。"</string> <string name="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string> - <string name="permdesc_backup" msgid="6912230525140589891">"允许应用程序控制系统的备份和还原机制。普通应用程序不能使用此权限。"</string> + <string name="permdesc_backup" msgid="6912230525140589891">"允许应用控制系统的备份和还原机制。普通应用不能使用此权限。"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"确认完整备份或恢复操作"</string> - <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"允许应用程序启动完整备份确认用户界面。不用于任何应用程序。"</string> + <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"允许应用启动完整备份确认用户界面。不用于任何应用。"</string> <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"显示未授权的窗口"</string> - <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"允许该应用程序创建供内部系统用户界面使用的窗口。普通应用程序不应使用此权限。"</string> + <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"允许该应用创建供内部系统用户界面使用的窗口。普通应用不应使用此权限。"</string> <string name="permlab_systemAlertWindow" msgid="3372321942941168324">"显示系统级警报"</string> - <string name="permdesc_systemAlertWindow" msgid="8507863469978066409">"允许应用程序显示系统警告窗口。恶意应用程序可能会控制整个屏幕。"</string> + <string name="permdesc_systemAlertWindow" msgid="8507863469978066409">"允许应用显示系统警告窗口。恶意应用可能会控制整个屏幕。"</string> <string name="permlab_setAnimationScale" msgid="2805103241153907174">"修改全局动画速度"</string> - <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"允许该应用程序随时更改全局动画速度(加快或减慢)。"</string> - <string name="permlab_manageAppTokens" msgid="1286505717050121370">"管理应用程序令牌"</string> - <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"允许应用程序绕过其正常的 Z 排序创建和管理自己的令牌。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"允许该应用随时更改全局动画速度(加快或减慢)。"</string> + <string name="permlab_manageAppTokens" msgid="1286505717050121370">"管理应用令牌"</string> + <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"允许应用绕过其正常的 Z 排序创建和管理自己的令牌。普通应用绝不需要此权限。"</string> <string name="permlab_injectEvents" msgid="1378746584023586600">"按键和控制按钮"</string> - <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"允许应用程序将自身的输入活动(例如按键操作等)提供给其他应用程序。恶意应用程序可能借此控制平板电脑。"</string> - <string name="permdesc_injectEvents" product="default" msgid="653128057572326253">"允许应用程序将自身的输入活动(例如按键操作等)提供给其他应用程序。恶意应用程序可能借此控制手机。"</string> + <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"允许应用将自身的输入活动(例如按键操作等)提供给其他应用。恶意应用可能借此控制平板电脑。"</string> + <string name="permdesc_injectEvents" product="default" msgid="653128057572326253">"允许应用将自身的输入活动(例如按键操作等)提供给其他应用。恶意应用可能借此控制手机。"</string> <string name="permlab_readInputState" msgid="469428900041249234">"记录您键入的内容和执行的操作"</string> - <string name="permdesc_readInputState" msgid="8387754901688728043">"允许应用程序记录您所按的键,包括与其他应用程序进行交互(如输入密码)时按的键。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_readInputState" msgid="8387754901688728043">"允许应用记录您所按的键,包括与其他应用进行交互(如输入密码)时按的键。普通应用绝不需要此权限。"</string> <string name="permlab_bindInputMethod" msgid="3360064620230515776">"绑定至输入法"</string> - <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允许用户绑定至输入法的顶级接口。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允许用户绑定至输入法的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"绑定至文字服务"</string> - <string name="permdesc_bindTextService" msgid="8151968910973998670">"允许用户绑定至文字服务(如 SpellCheckerService)的顶级接口。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindTextService" msgid="8151968910973998670">"允许用户绑定至文字服务(如 SpellCheckerService)的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"绑定到 VPN 服务"</string> - <string name="permdesc_bindVpnService" msgid="2067845564581693905">"允许用户绑定到 VPN 服务的顶级接口。普通应用程序从不需要此权限。"</string> + <string name="permdesc_bindVpnService" msgid="2067845564581693905">"允许用户绑定到 VPN 服务的顶级接口。普通应用从不需要此权限。"</string> <string name="permlab_bindWallpaper" msgid="8716400279937856462">"绑定到壁纸"</string> - <string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允许用户绑定到壁纸的顶级接口。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允许用户绑定到壁纸的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"绑定到窗口小部件服务"</string> - <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允许用户绑定到窗口小部件服务的顶级接口。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允许用户绑定到窗口小部件服务的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"与设备管理器交互"</string> - <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"允许用户将意向发送给设备管理员。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"允许用户将意向发送给设备管理员。普通应用绝不需要此权限。"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string> - <string name="permdesc_setOrientation" msgid="3046126619316671476">"允许应用程序随时更改屏幕的旋转状态。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_setOrientation" msgid="3046126619316671476">"允许应用随时更改屏幕的旋转状态。普通应用绝不需要此权限。"</string> <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"更改指针速度"</string> - <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"允许应用程序随时更改鼠标或触控板指针速度。普通应用程序绝不需要此权限。"</string> - <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"向应用程序发送 Linux 信号"</string> - <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"允许应用程序请求将提供的信号发送给所有持续的进程。"</string> - <string name="permlab_persistentActivity" msgid="8841113627955563938">"让应用程序始终运行"</string> - <string name="permdesc_persistentActivity" msgid="4909910271316074418">"允许应用程序持续保留其自身的某些组件,这样系统就无法将其用于其他应用程序。"</string> - <string name="permlab_deletePackages" msgid="184385129537705938">"删除应用程序"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"允许应用程序删除 Android 程序包。恶意应用程序可能借此删除重要的应用程序。"</string> - <string name="permlab_clearAppUserData" msgid="274109191845842756">"删除其他应用程序的数据"</string> - <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"允许应用程序清除用户数据。"</string> - <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"删除其他应用程序的缓存"</string> - <string name="permdesc_deleteCacheFiles" msgid="3812998599006730196">"允许应用程序删除缓存文件。"</string> - <string name="permlab_getPackageSize" msgid="7472921768357981986">"计算应用程序存储空间"</string> - <string name="permdesc_getPackageSize" msgid="3921068154420738296">"允许应用程序检索其代码、数据和缓存大小"</string> - <string name="permlab_installPackages" msgid="2199128482820306924">"直接安装应用程序"</string> - <string name="permdesc_installPackages" msgid="5628530972548071284">"允许应用程序安装新的或更新的 Android 程序包。恶意应用程序可能借此添加具有任意权限的新应用程序。"</string> - <string name="permlab_clearAppCache" msgid="7487279391723526815">"删除所有应用程序缓存数据"</string> - <string name="permdesc_clearAppCache" product="tablet" msgid="3523396284474042284">"允许应用程序删除应用程序缓存目录中的文件,从而释放平板电脑的存储空间。对系统进程的访问权限通常受到很大的限制。"</string> - <string name="permdesc_clearAppCache" product="default" msgid="5067988373366292186">"允许应用程序删除应用程序缓存目录中的文件,从而释放手机的存储空间。对系统进程的访问权限通常受到很大的限制。"</string> - <string name="permlab_movePackage" msgid="3289890271645921411">"移动应用程序资源"</string> - <string name="permdesc_movePackage" msgid="319562217778244524">"允许应用程序在内部与外部媒体之间移动应用程序资源。"</string> + <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"允许应用随时更改鼠标或触控板指针速度。普通应用绝不需要此权限。"</string> + <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"向应用发送 Linux 信号"</string> + <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"允许应用请求将提供的信号发送给所有持续的进程。"</string> + <string name="permlab_persistentActivity" msgid="8841113627955563938">"让应用始终运行"</string> + <string name="permdesc_persistentActivity" msgid="4909910271316074418">"允许应用持续保留其自身的某些组件,这样系统就无法将其用于其他应用。"</string> + <string name="permlab_deletePackages" msgid="184385129537705938">"删除应用"</string> + <string name="permdesc_deletePackages" msgid="7411480275167205081">"允许应用删除 Android 程序包。恶意应用可能借此删除重要的应用。"</string> + <string name="permlab_clearAppUserData" msgid="274109191845842756">"删除其他应用的数据"</string> + <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"允许应用清除用户数据。"</string> + <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"删除其他应用的缓存"</string> + <string name="permdesc_deleteCacheFiles" msgid="3812998599006730196">"允许应用删除缓存文件。"</string> + <string name="permlab_getPackageSize" msgid="7472921768357981986">"计算应用存储空间"</string> + <string name="permdesc_getPackageSize" msgid="3921068154420738296">"允许应用检索其代码、数据和缓存大小"</string> + <string name="permlab_installPackages" msgid="2199128482820306924">"直接安装应用"</string> + <string name="permdesc_installPackages" msgid="5628530972548071284">"允许应用安装新的或更新的 Android 程序包。恶意应用可能借此添加具有任意权限的新应用。"</string> + <string name="permlab_clearAppCache" msgid="7487279391723526815">"删除所有应用缓存数据"</string> + <string name="permdesc_clearAppCache" product="tablet" msgid="3523396284474042284">"允许应用删除应用缓存目录中的文件,从而释放平板电脑的存储空间。对系统进程的访问权限通常受到很大的限制。"</string> + <string name="permdesc_clearAppCache" product="default" msgid="5067988373366292186">"允许应用删除应用缓存目录中的文件,从而释放手机的存储空间。对系统进程的访问权限通常受到很大的限制。"</string> + <string name="permlab_movePackage" msgid="3289890271645921411">"移动应用资源"</string> + <string name="permdesc_movePackage" msgid="319562217778244524">"允许应用在内部与外部媒体之间移动应用资源。"</string> <string name="permlab_readLogs" msgid="6615778543198967614">"查阅敏感日志数据"</string> - <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"允许应用程序从系统的各种日志文件中读取信息。这样,应用程序就可以发现关于您平板电脑使用情况的一般信息,其中可能包含个人信息或隐私信息。"</string> - <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"允许应用程序从系统的各个日志文件中读取信息。这样,应用程序就可以发现关于您手机使用情况的一般信息,其中可能包含个人信息或隐私信息。"</string> + <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"允许应用从系统的各种日志文件中读取信息。这样,应用就可以发现关于您平板电脑使用情况的一般信息,其中可能包含个人信息或隐私信息。"</string> + <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"允许应用从系统的各个日志文件中读取信息。这样,应用就可以发现关于您手机使用情况的一般信息,其中可能包含个人信息或隐私信息。"</string> <string name="permlab_anyCodecForPlayback" msgid="715805555823881818">"使用任何媒体解码器进行播放"</string> - <string name="permdesc_anyCodecForPlayback" msgid="8283912488433189010">"允许该应用程序使用任何已安装的媒体解码器进行解码,以便播放媒体。"</string> + <string name="permdesc_anyCodecForPlayback" msgid="8283912488433189010">"允许该应用使用任何已安装的媒体解码器进行解码,以便播放媒体。"</string> <string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string> - <string name="permdesc_diagnostic" msgid="6608295692002452283">"允许应用程序读取/写入诊断组拥有的所有资源(例如 /dev 中的文件)。这可能会影响系统的稳定性和安全性。此权限仅供制造商或运营商诊断硬件方面的问题时使用。"</string> - <string name="permlab_changeComponentState" msgid="6335576775711095931">"启用或停用应用程序组件"</string> - <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"允许应用程序启用或停用其他应用程序的组件。恶意应用程序可能借此停用重要的平板电脑功能。请务必谨慎使用此权限,因为这可能导致某些应用程序组件处于无法使用、不一致或不稳定的状态。"</string> - <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"允许应用程序启用或停用其他应用程序的组件。恶意应用程序可能借此停用重要的手机功能。请务必谨慎使用此权限,因为这可能导致某些应用程序组件进入无法使用、不一致或不稳定的状态。"</string> + <string name="permdesc_diagnostic" msgid="6608295692002452283">"允许应用读取/写入诊断组拥有的所有资源(例如 /dev 中的文件)。这可能会影响系统的稳定性和安全性。此权限仅供制造商或运营商诊断硬件方面的问题时使用。"</string> + <string name="permlab_changeComponentState" msgid="6335576775711095931">"启用或停用应用组件"</string> + <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"允许应用启用或停用其他应用的组件。恶意应用可能借此停用重要的平板电脑功能。请务必谨慎使用此权限,因为这可能导致某些应用组件处于无法使用、不一致或不稳定的状态。"</string> + <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"允许应用启用或停用其他应用的组件。恶意应用可能借此停用重要的手机功能。请务必谨慎使用此权限,因为这可能导致某些应用组件进入无法使用、不一致或不稳定的状态。"</string> <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"授予或撤消权限"</string> <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"允许应用针对自身或其他应用授予或撤消特定权限。恶意应用可能会借此访问您未授权它们访问的功能。"</string> - <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"设置首选应用程序"</string> - <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"允许应用程序修改您的首选应用程序。恶意应用程序可能会在后台更改运行的应用程序,欺骗您现有的应用程序,以收集您的私人数据。"</string> + <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"设置首选应用"</string> + <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"允许应用修改您的首选应用。恶意应用可能会在后台更改运行的应用,欺骗您现有的应用,以收集您的私人数据。"</string> <string name="permlab_writeSettings" msgid="1365523497395143704">"修改全局系统设置"</string> - <string name="permdesc_writeSettings" msgid="7775723441558907181">"允许应用程序修改系统的设置数据。恶意应用程序可能会破坏您的系统配置。"</string> + <string name="permdesc_writeSettings" msgid="7775723441558907181">"允许应用修改系统的设置数据。恶意应用可能会破坏您的系统配置。"</string> <string name="permlab_writeSecureSettings" msgid="204676251876718288">"修改安全系统设置"</string> - <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"允许应用程序修改系统的安全设置数据。普通应用程序不能使用此权限。"</string> + <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"允许应用修改系统的安全设置数据。普通应用不能使用此权限。"</string> <string name="permlab_writeGservices" msgid="2149426664226152185">"修改 Google 服务地图"</string> - <string name="permdesc_writeGservices" msgid="1287309437638380229">"允许应用程序修改 Google 服务地图。普通应用程序不能使用此权限。"</string> + <string name="permdesc_writeGservices" msgid="1287309437638380229">"允许应用修改 Google 服务地图。普通应用不能使用此权限。"</string> <string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"开机时自动启动"</string> - <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"允许应用程序在系统完成引导后立即自动启动。这样可能会延长平板电脑的启动时间,并允许应用程序始终运行,从而导致平板电脑总体运行速度减慢。"</string> - <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"允许应用程序在系统完成引导后立即自动启动。这样可能会延长手机的启动时间,并允许应用程序始终运行,从而导致手机总体运行速度减慢。"</string> + <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"允许应用在系统完成引导后立即自动启动。这样可能会延长平板电脑的启动时间,并允许应用始终运行,从而导致平板电脑总体运行速度减慢。"</string> + <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"允许应用在系统完成引导后立即自动启动。这样可能会延长手机的启动时间,并允许应用始终运行,从而导致手机总体运行速度减慢。"</string> <string name="permlab_broadcastSticky" msgid="7919126372606881614">"发送持久广播"</string> - <string name="permdesc_broadcastSticky" product="tablet" msgid="1181582512022829259">"允许应用程序发送持久广播,此类广播在结束后仍会保留。恶意应用程序可能会导致平板电脑使用过多内存,从而使速度变慢或变得不稳定。"</string> - <string name="permdesc_broadcastSticky" product="default" msgid="3287869131621514325">"允许应用程序发送持久广播,此类广播在结束后仍会保留。恶意应用程序可能会导致手机使用过多内存,从而使速度变慢或变得不稳定。"</string> + <string name="permdesc_broadcastSticky" product="tablet" msgid="1181582512022829259">"允许应用发送持久广播,此类广播在结束后仍会保留。恶意应用可能会导致平板电脑使用过多内存,从而使速度变慢或变得不稳定。"</string> + <string name="permdesc_broadcastSticky" product="default" msgid="3287869131621514325">"允许应用发送持久广播,此类广播在结束后仍会保留。恶意应用可能会导致手机使用过多内存,从而使速度变慢或变得不稳定。"</string> <string name="permlab_readContacts" msgid="6219652189510218240">"读取联系人数据"</string> - <string name="permdesc_readContacts" product="tablet" msgid="4028657556924039119">"允许应用程序读取您平板电脑上存储的所有联系人(地址)数据。恶意应用程序可能借此将您的数据发送给其他人。"</string> - <string name="permdesc_readContacts" product="default" msgid="2032222056456498547">"允许应用程序读取您手机上存储的所有联系人(地址)数据。恶意应用程序可能借此将您的数据发送给其他人。"</string> + <string name="permdesc_readContacts" product="tablet" msgid="4028657556924039119">"允许应用读取您平板电脑上存储的所有联系人(地址)数据。恶意应用可能借此将您的数据发送给其他人。"</string> + <string name="permdesc_readContacts" product="default" msgid="2032222056456498547">"允许应用读取您手机上存储的所有联系人(地址)数据。恶意应用可能借此将您的数据发送给其他人。"</string> <string name="permlab_writeContacts" msgid="644616215860933284">"写入联系数据"</string> - <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"允许应用程序读取您平板电脑上存储的联系人(地址)数据。恶意应用程序可能借此清除或修改您的联系人数据。"</string> - <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"允许应用程序修改您手机上存储的联系人(地址)数据。恶意应用程序可能借此清除或修改您的联系人数据。"</string> + <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"允许应用读取您平板电脑上存储的联系人(地址)数据。恶意应用可能借此清除或修改您的联系人数据。"</string> + <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"允许应用修改您手机上存储的联系人(地址)数据。恶意应用可能借此清除或修改您的联系人数据。"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"读取通话记录"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"允许该应用读取平板电脑的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此将您的数据发送给他人。"</string> <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"允许该应用读取手机的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此将您的数据发送给他人。"</string> @@ -342,189 +342,189 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允许该应用修改平板电脑的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允许该应用修改手机的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string> <string name="permlab_readProfile" msgid="6824681438529842282">"读取您的个人资料数据"</string> - <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"允许应用程序读取您设备上存储的个人资料信息,例如您的姓名和联系信息。这意味着应用程序可以识别您的身份,并将您的个人资料信息发送给他人。"</string> + <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"允许应用读取您设备上存储的个人资料信息,例如您的姓名和联系信息。这意味着应用可以识别您的身份,并将您的个人资料信息发送给他人。"</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"写入到您的个人资料数据"</string> - <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"允许应用程序更改或添加您设备上存储的个人资料信息,例如您的姓名和联系信息。这意味着其他应用程序可以识别您的身份,并将您的个人资料信息发送给他人。"</string> + <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"允许应用更改或添加您设备上存储的个人资料信息,例如您的姓名和联系信息。这意味着其他应用可以识别您的身份,并将您的个人资料信息发送给他人。"</string> <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"读取您的社交视频流"</string> - <string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">"允许应用程序访问并同步您和好友的社交最新动态。恶意应用程序可能借此读取您与社交网络上的好友之间的私人交流信息。"</string> + <string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">"允许应用访问并同步您和好友的社交最新动态。恶意应用可能借此读取您与社交网络上的好友之间的私人交流信息。"</string> <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"写入您的社交视频流"</string> - <string name="permdesc_writeSocialStream" product="default" msgid="3496277176955721451">"允许应用程序显示好友的社交最新动态。恶意应用程序可能借此冒充您的某位好友,诱骗您泄露密码或其他机密信息。"</string> + <string name="permdesc_writeSocialStream" product="default" msgid="3496277176955721451">"允许应用显示好友的社交最新动态。恶意应用可能借此冒充您的某位好友,诱骗您泄露密码或其他机密信息。"</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"读取日历活动和机密信息"</string> - <string name="permdesc_readCalendar" product="tablet" msgid="2338414551004122687">"允许应用程序读取您平板电脑上存储的所有日历活动,包括朋友或同事的活动。恶意应用程序可能会在所有者不知情的情况下,从这些日历活动中提取个人信息。"</string> - <string name="permdesc_readCalendar" product="default" msgid="5693933067751827753">"允许应用程序读取您手机上存储的所有日历活动,包括朋友或同事的活动。恶意应用程序可能会在所有者不知情的情况下,从这些日历活动中提取个人信息。"</string> + <string name="permdesc_readCalendar" product="tablet" msgid="2338414551004122687">"允许应用读取您平板电脑上存储的所有日历活动,包括朋友或同事的活动。恶意应用可能会在所有者不知情的情况下,从这些日历活动中提取个人信息。"</string> + <string name="permdesc_readCalendar" product="default" msgid="5693933067751827753">"允许应用读取您手机上存储的所有日历活动,包括朋友或同事的活动。恶意应用可能会在所有者不知情的情况下,从这些日历活动中提取个人信息。"</string> <string name="permlab_writeCalendar" msgid="8438874755193825647">"添加或修改日历活动,并在所有者不知情的情况下向邀请对象发送电子邮件"</string> - <string name="permdesc_writeCalendar" msgid="2243771395254848873">"允许应用程序以日历所有者的身份发送活动邀请,并添加、删除和更改您可在自己设备上修改的活动,包括朋友或同事的活动。恶意应用程序可能会冒充日历所有者发送垃圾邮件,在所有者不知情的情况下修改活动,或者添加虚假活动。"</string> + <string name="permdesc_writeCalendar" msgid="2243771395254848873">"允许应用以日历所有者的身份发送活动邀请,并添加、删除和更改您可在自己设备上修改的活动,包括朋友或同事的活动。恶意应用可能会冒充日历所有者发送垃圾邮件,在所有者不知情的情况下修改活动,或者添加虚假活动。"</string> <string name="permlab_accessMockLocation" msgid="8688334974036823330">"使用模拟地点来源进行测试"</string> - <string name="permdesc_accessMockLocation" msgid="7577931556422993949">"允许应用程序创建模拟位置源以进行测试。恶意应用程序可能借此覆盖由真实位置源(如 GPS 或网络服务提供商)返回的位置和/或状态信息。"</string> + <string name="permdesc_accessMockLocation" msgid="7577931556422993949">"允许应用创建模拟位置源以进行测试。恶意应用可能借此覆盖由真实位置源(如 GPS 或网络服务提供商)返回的位置和/或状态信息。"</string> <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"获取额外的位置信息提供程序命令"</string> - <string name="permdesc_accessLocationExtraCommands" msgid="6737736970602176133">"允许应用程序访问额外的位置提供程序命令。恶意应用程序可能借此干扰 GPS 或其他位置源的运行。"</string> + <string name="permdesc_accessLocationExtraCommands" msgid="6737736970602176133">"允许应用访问额外的位置提供程序命令。恶意应用可能借此干扰 GPS 或其他位置源的运行。"</string> <string name="permlab_installLocationProvider" msgid="6578101199825193873">"允许安装位置信息提供程序"</string> - <string name="permdesc_installLocationProvider" msgid="1742577679350078373">"创建模拟位置源以进行测试。恶意应用程序可能借此覆盖由真实位置源(如 GPS 或网络服务提供商)返回的位置和/或状态信息,或者监视您的位置并将信息提供给外部源。"</string> + <string name="permdesc_installLocationProvider" msgid="1742577679350078373">"创建模拟位置源以进行测试。恶意应用可能借此覆盖由真实位置源(如 GPS 或网络服务提供商)返回的位置和/或状态信息,或者监视您的位置并将信息提供给外部源。"</string> <string name="permlab_accessFineLocation" msgid="8116127007541369477">"精准的(GPS)位置"</string> - <string name="permdesc_accessFineLocation" product="tablet" msgid="5326423948268164934">"访问精准的位置源,例如平板电脑上的全球定位系统(如果有)。恶意应用程序可能借此确定您所处的位置,并可能消耗额外的电池电量。"</string> - <string name="permdesc_accessFineLocation" product="default" msgid="7130267914433890869">"访问手机上的精准位置源,例如全球定位系统(如果有)。恶意应用程序可能借此确定您所处的位置,并可能消耗额外的电池电量。"</string> + <string name="permdesc_accessFineLocation" product="tablet" msgid="5326423948268164934">"访问精准的位置源,例如平板电脑上的全球定位系统(如果有)。恶意应用可能借此确定您所处的位置,并可能消耗额外的电池电量。"</string> + <string name="permdesc_accessFineLocation" product="default" msgid="7130267914433890869">"访问手机上的精准位置源,例如全球定位系统(如果有)。恶意应用可能借此确定您所处的位置,并可能消耗额外的电池电量。"</string> <string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"(基于网络的)粗略位置"</string> - <string name="permdesc_accessCoarseLocation" product="tablet" msgid="5460726396318105483">"访问粗略的位置源(例如蜂窝网络数据库)以确定平板电脑的大体位置(如果可以)。恶意应用程序可能借此确定您所处的大体位置。"</string> - <string name="permdesc_accessCoarseLocation" product="default" msgid="8900795778057579522">"访问粗略的位置源(例如蜂窝网络数据库)以确定手机的大体位置(如果可以)。恶意应用程序可能借此确定您所处的大体位置。"</string> + <string name="permdesc_accessCoarseLocation" product="tablet" msgid="5460726396318105483">"访问粗略的位置源(例如蜂窝网络数据库)以确定平板电脑的大体位置(如果可以)。恶意应用可能借此确定您所处的大体位置。"</string> + <string name="permdesc_accessCoarseLocation" product="default" msgid="8900795778057579522">"访问粗略的位置源(例如蜂窝网络数据库)以确定手机的大体位置(如果可以)。恶意应用可能借此确定您所处的大体位置。"</string> <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"访问 SurfaceFlinger"</string> - <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允许应用程序使用 SurfaceFlinger 低级功能。"</string> + <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允许应用使用 SurfaceFlinger 低级功能。"</string> <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"读取帧缓冲区"</string> - <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允许应用程序读取帧缓冲区的内容。"</string> + <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允许应用读取帧缓冲区的内容。"</string> <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改您的音频设置"</string> - <string name="permdesc_modifyAudioSettings" msgid="7343951185408396919">"允许应用程序修改全局音频设置,如音量和路由。"</string> + <string name="permdesc_modifyAudioSettings" msgid="7343951185408396919">"允许应用修改全局音频设置,如音量和路由。"</string> <string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string> - <string name="permdesc_recordAudio" msgid="2387462233976248635">"允许应用程序访问录音路径。"</string> + <string name="permdesc_recordAudio" msgid="2387462233976248635">"允许应用访问录音路径。"</string> <string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string> - <string name="permdesc_camera" msgid="1507407407002492176">"允许应用程序使用相机拍摄照片和视频。该权限可让应用程序随时收集相机的取景。"</string> + <string name="permdesc_camera" msgid="1507407407002492176">"允许应用使用相机拍摄照片和视频。该权限可让应用随时收集相机的取景。"</string> <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"永久停用平板电脑"</string> <string name="permlab_brick" product="default" msgid="8337817093326370537">"永久停用手机"</string> - <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"允许应用程序永久停用整个平板电脑,但这样非常危险。"</string> - <string name="permdesc_brick" product="default" msgid="5788903297627283099">"允许应用程序永久停用整个手机,但这非常危险。"</string> + <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"允许应用永久停用整个平板电脑,但这样非常危险。"</string> + <string name="permdesc_brick" product="default" msgid="5788903297627283099">"允许应用永久停用整个手机,但这非常危险。"</string> <string name="permlab_reboot" product="tablet" msgid="3436634972561795002">"强行重新启动平板电脑"</string> <string name="permlab_reboot" product="default" msgid="2898560872462638242">"强行重新启动手机"</string> - <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"允许应用程序强行重新启动平板电脑。"</string> - <string name="permdesc_reboot" product="default" msgid="5326008124289989969">"允许应用程序强行重新启动手机。"</string> + <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"允许应用强行重新启动平板电脑。"</string> + <string name="permdesc_reboot" product="default" msgid="5326008124289989969">"允许应用强行重新启动手机。"</string> <string name="permlab_mount_unmount_filesystems" msgid="1761023272170956541">"装载和卸载文件系统"</string> - <string name="permdesc_mount_unmount_filesystems" msgid="1829290701658992347">"允许应用程序装载和卸载可移动存储设备的文件系统。"</string> + <string name="permdesc_mount_unmount_filesystems" msgid="1829290701658992347">"允许应用装载和卸载可移动存储设备的文件系统。"</string> <string name="permlab_mount_format_filesystems" msgid="5523285143576718981">"格式化外部存储设备"</string> - <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"允许应用程序格式化可移动存储设备。"</string> + <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"允许应用格式化可移动存储设备。"</string> <string name="permlab_asec_access" msgid="3411338632002193846">"获取有关内部存储设备的信息"</string> - <string name="permdesc_asec_access" msgid="3094563844593878548">"允许应用程序获取有关内存设备的信息。"</string> + <string name="permdesc_asec_access" msgid="3094563844593878548">"允许应用获取有关内存设备的信息。"</string> <string name="permlab_asec_create" msgid="6414757234789336327">"创建内部存储设备"</string> - <string name="permdesc_asec_create" msgid="4558869273585856876">"允许应用程序创建内存设备。"</string> + <string name="permdesc_asec_create" msgid="4558869273585856876">"允许应用创建内存设备。"</string> <string name="permlab_asec_destroy" msgid="526928328301618022">"清除内部存储设备"</string> - <string name="permdesc_asec_destroy" msgid="7218749286145526537">"允许应用程序清除内存设备。"</string> + <string name="permdesc_asec_destroy" msgid="7218749286145526537">"允许应用清除内存设备。"</string> <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"装载/卸载内存设备"</string> - <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"允许应用程序装载/卸载内存设备。"</string> + <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"允许应用装载/卸载内存设备。"</string> <string name="permlab_asec_rename" msgid="7496633954080472417">"重命名内部存储设备"</string> - <string name="permdesc_asec_rename" msgid="1794757588472127675">"允许应用程序重命名内存设备。"</string> + <string name="permdesc_asec_rename" msgid="1794757588472127675">"允许应用重命名内存设备。"</string> <string name="permlab_vibrate" msgid="7768356019980849603">"控制振动器"</string> - <string name="permdesc_vibrate" msgid="6284989245902300945">"允许应用程序控制振动器。"</string> + <string name="permdesc_vibrate" msgid="6284989245902300945">"允许应用控制振动器。"</string> <string name="permlab_flashlight" msgid="2155920810121984215">"控制闪光灯"</string> - <string name="permdesc_flashlight" msgid="6522284794568368310">"允许应用程序控制闪光灯。"</string> + <string name="permdesc_flashlight" msgid="6522284794568368310">"允许应用控制闪光灯。"</string> <string name="permlab_manageUsb" msgid="1113453430645402723">"管理 USB 设备的偏好设置和权限"</string> - <string name="permdesc_manageUsb" msgid="7776155430218239833">"允许应用程序管理针对 USB 设备的偏好设置和权限。"</string> + <string name="permdesc_manageUsb" msgid="7776155430218239833">"允许应用管理针对 USB 设备的偏好设置和权限。"</string> <string name="permlab_accessMtp" msgid="4953468676795917042">"应用 MTP 协议"</string> <string name="permdesc_accessMtp" msgid="6532961200486791570">"允许访问内核 MTP 驱动程序,以便应用 MTP USB 协议。"</string> <string name="permlab_hardware_test" msgid="4148290860400659146">"测试硬件"</string> - <string name="permdesc_hardware_test" msgid="6597964191208016605">"允许应用程序控制各种外围设备以进行硬件测试。"</string> + <string name="permdesc_hardware_test" msgid="6597964191208016605">"允许应用控制各种外围设备以进行硬件测试。"</string> <string name="permlab_callPhone" msgid="3925836347681847954">"直接拨打电话号码"</string> - <string name="permdesc_callPhone" msgid="6396463004110544744">"允许应用程序在没有您干预的情况下呼叫电话号码。恶意应用程序可能会产生意料之外的话费。请注意,此权限不允许应用程序呼叫紧急电话。"</string> + <string name="permdesc_callPhone" msgid="6396463004110544744">"允许应用在没有您干预的情况下呼叫电话号码。恶意应用可能会产生意料之外的话费。请注意,此权限不允许应用呼叫紧急电话。"</string> <string name="permlab_callPrivileged" msgid="4198349211108497879">"直接呼叫任何电话号码"</string> - <string name="permdesc_callPrivileged" msgid="1689024901509996810">"允许应用程序在没有您干预的情况下呼叫任何电话号码,包括紧急呼叫号码。恶意应用程序可能会向紧急服务进行多余以及非法呼叫。"</string> + <string name="permdesc_callPrivileged" msgid="1689024901509996810">"允许应用在没有您干预的情况下呼叫任何电话号码,包括紧急呼叫号码。恶意应用可能会向紧急服务进行多余以及非法呼叫。"</string> <string name="permlab_performCdmaProvisioning" product="tablet" msgid="4842576994144604821">"直接启动 CDMA 平板电脑设置"</string> <string name="permlab_performCdmaProvisioning" product="default" msgid="5604848095315421425">"直接启动 CDMA 电话设置"</string> - <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"允许应用程序启动 CDMA 配置。恶意应用程序可能会无端启动 CDMA 配置。"</string> + <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"允许应用启动 CDMA 配置。恶意应用可能会无端启动 CDMA 配置。"</string> <string name="permlab_locationUpdates" msgid="7785408253364335740">"控制位置更新通知"</string> - <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允许应用程序启用/停用来自无线装置的位置更新通知。普通应用程序不能使用此权限。"</string> + <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允许应用启用/停用来自无线装置的位置更新通知。普通应用不能使用此权限。"</string> <string name="permlab_checkinProperties" msgid="7855259461268734914">"访问检入属性"</string> - <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允许应用程序对登记服务上传的属性拥有读取/写入权限。普通应用程序不能使用此权限。"</string> + <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允许应用对登记服务上传的属性拥有读取/写入权限。普通应用不能使用此权限。"</string> <string name="permlab_bindGadget" msgid="776905339015863471">"选择窗口小部件"</string> - <string name="permdesc_bindGadget" msgid="8261326938599049290">"允许应用程序告知系统哪些窗口小部件可供哪个应用程序使用。拥有此权限的应用程序可向其他应用程序授予对个人资料的访问权限。普通应用程序不能使用此权限。"</string> + <string name="permdesc_bindGadget" msgid="8261326938599049290">"允许应用告知系统哪些窗口小部件可供哪个应用使用。拥有此权限的应用可向其他应用授予对个人资料的访问权限。普通应用不能使用此权限。"</string> <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"修改手机状态"</string> - <string name="permdesc_modifyPhoneState" msgid="1029877529007686732">"允许应用程序控制设备的电话功能。拥有此权限的应用程序可在不通知您的情况下执行切换网络、开关手机无线装置等此类操作。"</string> + <string name="permdesc_modifyPhoneState" msgid="1029877529007686732">"允许应用控制设备的电话功能。拥有此权限的应用可在不通知您的情况下执行切换网络、开关手机无线装置等此类操作。"</string> <string name="permlab_readPhoneState" msgid="2326172951448691631">"读取手机状态和身份"</string> - <string name="permdesc_readPhoneState" msgid="5127767618743602782">"允许应用程序访问设备的电话功能。拥有此权限的应用程序可以确定本机的电话号码和序列号、通话是否有效、呼叫的号码等。"</string> + <string name="permdesc_readPhoneState" msgid="5127767618743602782">"允许应用访问设备的电话功能。拥有此权限的应用可以确定本机的电话号码和序列号、通话是否有效、呼叫的号码等。"</string> <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"阻止平板电脑进入休眠状态"</string> <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"防止手机休眠"</string> - <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"允许应用程序阻止平板电脑进入休眠状态。"</string> - <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"允许应用程序阻止手机进入休眠状态。"</string> + <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"允许应用阻止平板电脑进入休眠状态。"</string> + <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"允许应用阻止手机进入休眠状态。"</string> <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"打开或关闭平板电脑"</string> <string name="permlab_devicePower" product="default" msgid="4928622470980943206">"开机或关机"</string> - <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"允许应用程序打开或关闭平板电脑。"</string> - <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"允许应用程序打开或关闭手机。"</string> + <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"允许应用打开或关闭平板电脑。"</string> + <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"允许应用打开或关闭手机。"</string> <string name="permlab_factoryTest" msgid="3715225492696416187">"在出厂测试模式下运行"</string> <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"作为低级制造商测试运行,从而允许对平板电脑硬件进行完全访问。此权限仅当平板电脑在制造商测试模式下运行时才可用。"</string> <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"作为一项低级制造商测试来运行,从而允许对手机硬件进行完全访问。此权限仅当手机在制造商测试模式下运行时才可用。。"</string> <string name="permlab_setWallpaper" msgid="6627192333373465143">"设置壁纸"</string> - <string name="permdesc_setWallpaper" msgid="7373447920977624745">"允许应用程序设置系统壁纸。"</string> + <string name="permdesc_setWallpaper" msgid="7373447920977624745">"允许应用设置系统壁纸。"</string> <string name="permlab_setWallpaperHints" msgid="3600721069353106851">"设置有关壁纸大小的提示"</string> - <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"允许应用程序设置有关系统壁纸大小的提示。"</string> + <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"允许应用设置有关系统壁纸大小的提示。"</string> <string name="permlab_masterClear" msgid="2315750423139697397">"将系统恢复为出厂设置"</string> - <string name="permdesc_masterClear" msgid="3665380492633910226">"允许应用程序将系统完全重置为其出厂设置,这会清除所有数据、配置和已安装的应用程序。"</string> + <string name="permdesc_masterClear" msgid="3665380492633910226">"允许应用将系统完全重置为其出厂设置,这会清除所有数据、配置和已安装的应用。"</string> <string name="permlab_setTime" msgid="2021614829591775646">"设置时间"</string> - <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"允许应用程序更改平板电脑的时间。"</string> - <string name="permdesc_setTime" product="default" msgid="1855702730738020">"允许应用程序更改手机的时间。"</string> + <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"允许应用更改平板电脑的时间。"</string> + <string name="permdesc_setTime" product="default" msgid="1855702730738020">"允许应用更改手机的时间。"</string> <string name="permlab_setTimeZone" msgid="2945079801013077340">"设置时区"</string> - <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"允许应用程序更改平板电脑的时区。"</string> - <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"允许应用程序更改手机的时区。"</string> + <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"允许应用更改平板电脑的时区。"</string> + <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"允许应用更改手机的时区。"</string> <string name="permlab_accountManagerService" msgid="4829262349691386986">"作为 AccountManagerService"</string> - <string name="permdesc_accountManagerService" msgid="1948455552333615954">"允许该应用程序调用 AccountAuthenticators。"</string> + <string name="permdesc_accountManagerService" msgid="1948455552333615954">"允许该应用调用 AccountAuthenticators。"</string> <string name="permlab_getAccounts" msgid="4549918644233460103">"发现已知帐户"</string> - <string name="permdesc_getAccounts" product="tablet" msgid="3238360555257773358">"允许应用程序获取平板电脑已知的帐户列表。"</string> - <string name="permdesc_getAccounts" product="default" msgid="2735689364629830348">"允许应用程序获取手机已知的帐户列表。"</string> + <string name="permdesc_getAccounts" product="tablet" msgid="3238360555257773358">"允许应用获取平板电脑已知的帐户列表。"</string> + <string name="permdesc_getAccounts" product="default" msgid="2735689364629830348">"允许应用获取手机已知的帐户列表。"</string> <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"作为帐户身份验证程序"</string> - <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"允许应用程序使用 AccountManager 的帐户身份验证程序功能,包括创建帐户以及获取和设置其密码。"</string> + <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"允许应用使用 AccountManager 的帐户身份验证程序功能,包括创建帐户以及获取和设置其密码。"</string> <string name="permlab_manageAccounts" msgid="4440380488312204365">"管理帐户列表"</string> - <string name="permdesc_manageAccounts" msgid="8698295625488292506">"允许应用程序执行添加帐户、删除帐户、删除帐户密码等操作。"</string> + <string name="permdesc_manageAccounts" msgid="8698295625488292506">"允许应用执行添加帐户、删除帐户、删除帐户密码等操作。"</string> <string name="permlab_useCredentials" msgid="6401886092818819856">"使用帐户的身份验证凭据"</string> - <string name="permdesc_useCredentials" msgid="7984227147403346422">"允许应用程序请求身份验证令牌。"</string> + <string name="permdesc_useCredentials" msgid="7984227147403346422">"允许应用请求身份验证令牌。"</string> <string name="permlab_accessNetworkState" msgid="6865575199464405769">"查看网络状态"</string> - <string name="permdesc_accessNetworkState" msgid="479772796952547198">"允许应用程序查看所有网络的状态。"</string> + <string name="permdesc_accessNetworkState" msgid="479772796952547198">"允许应用查看所有网络的状态。"</string> <string name="permlab_createNetworkSockets" msgid="9121633680349549585">"完全的互联网访问权限"</string> - <string name="permdesc_createNetworkSockets" msgid="5963922297444265950">"允许应用程序创建网络套接字。"</string> + <string name="permdesc_createNetworkSockets" msgid="5963922297444265950">"允许应用创建网络套接字。"</string> <string name="permlab_writeApnSettings" msgid="505660159675751896">"更改/拦截网络设置和流量"</string> - <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"允许应用程序更改网络设置,并拦截和检查所有网络流量,例如更改任意 APN 的代理和端口。恶意应用程序可能会在您不知情的情况下监视、重定向或修改网络数据包。"</string> + <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"允许应用更改网络设置,并拦截和检查所有网络流量,例如更改任意 APN 的代理和端口。恶意应用可能会在您不知情的情况下监视、重定向或修改网络数据包。"</string> <string name="permlab_changeNetworkState" msgid="958884291454327309">"更改网络连接性"</string> - <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允许应用程序更改网络连接的状态。"</string> + <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允许应用更改网络连接的状态。"</string> <string name="permlab_changeTetherState" msgid="5952584964373017960">"更改网络共享连接"</string> - <string name="permdesc_changeTetherState" msgid="1524441344412319780">"允许应用程序更改绑定网络连接的状态。"</string> + <string name="permdesc_changeTetherState" msgid="1524441344412319780">"允许应用更改绑定网络连接的状态。"</string> <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"更改后台数据使用设置"</string> - <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"允许应用程序更改后台数据使用设置。"</string> + <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"允许应用更改后台数据使用设置。"</string> <string name="permlab_accessWifiState" msgid="8100926650211034400">"查看 Wi-Fi 状态"</string> - <string name="permdesc_accessWifiState" msgid="7770452658226256831">"允许应用程序查看有关 Wi-Fi 状态的信息。"</string> + <string name="permdesc_accessWifiState" msgid="7770452658226256831">"允许应用查看有关 Wi-Fi 状态的信息。"</string> <string name="permlab_changeWifiState" msgid="7280632711057112137">"更改 Wi-Fi 状态"</string> - <string name="permdesc_changeWifiState" msgid="7399961004537946240">"允许应用程序与 Wi-Fi 接入点建立和断开连接,并对配置的 Wi-Fi 网络进行更改。"</string> + <string name="permdesc_changeWifiState" msgid="7399961004537946240">"允许应用与 Wi-Fi 接入点建立和断开连接,并对配置的 Wi-Fi 网络进行更改。"</string> <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"允许接收 Wi-Fi 多播"</string> - <string name="permdesc_changeWifiMulticastState" msgid="7633598524564320817">"允许应用程序接收并非直接发送至您设备的数据包。在查找附近提供的服务时,此权限很有用。这种操作所耗电量大于非多播模式。"</string> + <string name="permdesc_changeWifiMulticastState" msgid="7633598524564320817">"允许应用接收并非直接发送至您设备的数据包。在查找附近提供的服务时,此权限很有用。这种操作所耗电量大于非多播模式。"</string> <string name="permlab_bluetoothAdmin" msgid="3606576270792236062">"蓝牙管理"</string> - <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"允许应用程序配置本地蓝牙平板电脑,以及发现远程设备并进行配对。"</string> - <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"允许应用程序配置本地蓝牙手机,以及发现远程设备并进行配对。"</string> + <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"允许应用配置本地蓝牙平板电脑,以及发现远程设备并进行配对。"</string> + <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"允许应用配置本地蓝牙手机,以及发现远程设备并进行配对。"</string> <string name="permlab_accessWimaxState" msgid="1232061307208861588">"查看 WiMAX 状态"</string> - <string name="permdesc_accessWimaxState" msgid="5914958077555177749">"允许该应用程序查看有关 WiMAX 状态的信息。"</string> + <string name="permdesc_accessWimaxState" msgid="5914958077555177749">"允许该应用查看有关 WiMAX 状态的信息。"</string> <string name="permlab_changeWimaxState" msgid="2405042267131496579">"更改 WiMAX 状态"</string> - <string name="permdesc_changeWimaxState" msgid="3328853825006455912">"允许该应用程序连接到 WiMAX 网络以及从 WiMAX 网络断开连接。"</string> + <string name="permdesc_changeWimaxState" msgid="3328853825006455912">"允许该应用连接到 WiMAX 网络以及从 WiMAX 网络断开连接。"</string> <string name="permlab_bluetooth" msgid="8361038707857018732">"创建蓝牙连接"</string> - <string name="permdesc_bluetooth" product="tablet" msgid="7007851048416363446">"允许应用程序查看本地蓝牙平板电脑的配置,以及建立和接受与配对设备的连接。"</string> - <string name="permdesc_bluetooth" product="default" msgid="31846362767164948">"允许应用程序查看本地蓝牙手机的配置,以及建立和接受与配对设备的连接。"</string> + <string name="permdesc_bluetooth" product="tablet" msgid="7007851048416363446">"允许应用查看本地蓝牙平板电脑的配置,以及建立和接受与配对设备的连接。"</string> + <string name="permdesc_bluetooth" product="default" msgid="31846362767164948">"允许应用查看本地蓝牙手机的配置,以及建立和接受与配对设备的连接。"</string> <string name="permlab_nfc" msgid="4423351274757876953">"控制近距离通信"</string> - <string name="permdesc_nfc" msgid="7120611819401789907">"允许应用程序与近距离无线通信 (NFC) 标记、卡和阅读器进行通信。"</string> + <string name="permdesc_nfc" msgid="7120611819401789907">"允许应用与近距离无线通信 (NFC) 标记、卡和阅读器进行通信。"</string> <string name="permlab_disableKeyguard" msgid="4977406164311535092">"停用键锁"</string> - <string name="permdesc_disableKeyguard" msgid="6231611286892232626">"允许应用程序停用键锁和任何关联的密码安全设置。例如,在手机上接听电话时停用键锁,在通话结束后重新启用键锁。"</string> + <string name="permdesc_disableKeyguard" msgid="6231611286892232626">"允许应用停用键锁和任何关联的密码安全设置。例如,在手机上接听电话时停用键锁,在通话结束后重新启用键锁。"</string> <string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string> - <string name="permdesc_readSyncSettings" msgid="5464056785274229278">"允许应用程序读取同步设置,例如是否对“联系人”应用程序启用同步功能。"</string> + <string name="permdesc_readSyncSettings" msgid="5464056785274229278">"允许应用读取同步设置,例如是否对“联系人”应用启用同步功能。"</string> <string name="permlab_writeSyncSettings" msgid="6297138566442486462">"写入同步设置"</string> - <string name="permdesc_writeSyncSettings" msgid="1466056564502117130">"允许应用程序修改同步设置,例如是否对“联系人”应用程序启用同步功能。"</string> + <string name="permdesc_writeSyncSettings" msgid="1466056564502117130">"允许应用修改同步设置,例如是否对“联系人”应用启用同步功能。"</string> <string name="permlab_readSyncStats" msgid="7396577451360202448">"读取同步统计信息"</string> - <string name="permdesc_readSyncStats" msgid="3801971839939951678">"允许应用程序读取同步统计信息;例如,已发生的同步历史记录。"</string> + <string name="permdesc_readSyncStats" msgid="3801971839939951678">"允许应用读取同步统计信息;例如,已发生的同步历史记录。"</string> <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"读取订阅的供稿"</string> - <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"允许应用程序获取有关当前同步的 Feed 的详情。"</string> + <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"允许应用获取有关当前同步的 Feed 的详情。"</string> <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"写入订阅的供稿"</string> - <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"允许应用程序修改您当前同步的 Feed。恶意应用程序可能会更改您的同步 Feed。"</string> + <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"允许应用修改您当前同步的 Feed。恶意应用可能会更改您的同步 Feed。"</string> <string name="permlab_readDictionary" msgid="8410247960433376352">"读取用户定义的词典"</string> - <string name="permdesc_readDictionary" msgid="8977815988329283705">"允许应用程序读取用户可能在用户词典中已存储的任意私有字词、名称和短语。"</string> + <string name="permdesc_readDictionary" msgid="8977815988329283705">"允许应用读取用户可能在用户词典中已存储的任意私有字词、名称和短语。"</string> <string name="permlab_writeDictionary" msgid="2296383164914812772">"写入用户定义的词典"</string> - <string name="permdesc_writeDictionary" msgid="8185385716255065291">"允许应用程序向用户词典中写入新词。"</string> + <string name="permdesc_writeDictionary" msgid="8185385716255065291">"允许应用向用户词典中写入新词。"</string> <string name="permlab_sdcardRead" product="nosdcard" msgid="4086221374639183281">"读取 USB 存储设备的内容"</string> <string name="permlab_sdcardRead" product="default" msgid="8537875151845139539">"读取 SD 卡的内容"</string> <string name="permdesc_sdcardRead" product="nosdcard" msgid="1055302898999352339">"允许应用读取 USB 存储设备的内容。"</string> <string name="permdesc_sdcardRead" product="default" msgid="7947792373570683542">"允许应用读取 SD 卡的内容。"</string> <string name="permlab_sdcardWrite" product="nosdcard" msgid="85430876310764752">"修改/删除 USB 存储设备内容"</string> <string name="permlab_sdcardWrite" product="default" msgid="8079403759001777291">"修改/删除 SD 卡中的内容"</string> - <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"允许应用程序写入 USB 存储设备。"</string> - <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用程序写入 SD 卡。"</string> + <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"允许应用写入 USB 存储设备。"</string> + <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入 SD 卡。"</string> <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/删除内部媒体存储设备的内容"</string> - <string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用程序修改内部媒体存储设备的内容。"</string> + <string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用修改内部媒体存储设备的内容。"</string> <string name="permlab_cache_filesystem" msgid="5656487264819669824">"访问缓存文件系统"</string> - <string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允许应用程序读取和写入缓存文件系统。"</string> + <string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允许应用读取和写入缓存文件系统。"</string> <string name="permlab_use_sip" msgid="5986952362795870502">"拨打/接听互联网通话"</string> - <string name="permdesc_use_sip" msgid="4717632000062674294">"允许应用程序使用 SIP 服务拨打/接听互联网电话。"</string> + <string name="permdesc_use_sip" msgid="4717632000062674294">"允许应用使用 SIP 服务拨打/接听互联网电话。"</string> <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"读取网络使用情况历史记录"</string> - <string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"允许应用程序读取特定网络和应用程序的网络使用情况历史记录。"</string> + <string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"允许应用读取特定网络和应用的网络使用情况历史记录。"</string> <string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"管理网络政策"</string> - <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允许应用程序管理网络政策和定义专门针对应用程序的规则。"</string> + <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允许应用管理网络政策和定义专门针对应用的规则。"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"修改网络使用情况记录方式"</string> - <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允许该应用程序修改对于各应用程序的网络使用情况的统计方式。普通应用程序不应使用此权限。"</string> + <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允许该应用修改对于各应用的网络使用情况的统计方式。普通应用不应使用此权限。"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"设置密码规则"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"控制屏幕解锁密码所允许的长度和字符。"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"监视屏幕解锁尝试次数"</string> @@ -542,7 +542,7 @@ <string name="policylab_expirePassword" msgid="885279151847254056">"设置锁定屏幕密码的有效期"</string> <string name="policydesc_expirePassword" msgid="1729725226314691591">"控制系统强制用户更改屏幕锁定密码的频率。"</string> <string name="policylab_encryptedStorage" msgid="8901326199909132915">"设置存储设备加密"</string> - <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"要求对存储的应用程序数据进行加密。"</string> + <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"要求对存储的应用数据进行加密。"</string> <string name="policylab_disableCamera" msgid="6395301023152297826">"停用相机"</string> <string name="policydesc_disableCamera" msgid="2306349042834754597">"禁止使用所有设备摄像头。"</string> <string-array name="phoneTypes"> @@ -760,20 +760,20 @@ <string name="autofill_area" msgid="3547409050889952423">"区域"</string> <string name="autofill_emirate" msgid="2893880978835698818">"酋长国"</string> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string> - <string name="permdesc_readHistoryBookmarks" msgid="4577476392604595921">"允许应用程序读取“浏览器”访问过的所有网址,以及“浏览器”的所有书签。"</string> + <string name="permdesc_readHistoryBookmarks" msgid="4577476392604595921">"允许应用读取“浏览器”访问过的所有网址,以及“浏览器”的所有书签。"</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string> - <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="1757103804824209530">"允许应用程序修改“浏览器”存储在平板电脑上的历史记录或书签。恶意应用程序可能借此删除或修改“浏览器”的数据。"</string> - <string name="permdesc_writeHistoryBookmarks" product="default" msgid="6693764355720719197">"允许应用程序修改“浏览器”存储在手机上的历史记录或书签。恶意应用程序可能借此删除或修改“浏览器”的数据。"</string> + <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="1757103804824209530">"允许应用修改“浏览器”存储在平板电脑上的历史记录或书签。恶意应用可能借此删除或修改“浏览器”的数据。"</string> + <string name="permdesc_writeHistoryBookmarks" product="default" msgid="6693764355720719197">"允许应用修改“浏览器”存储在手机上的历史记录或书签。恶意应用可能借此删除或修改“浏览器”的数据。"</string> <string name="permlab_setAlarm" msgid="5924401328803615165">"在闹钟中设置警报"</string> - <string name="permdesc_setAlarm" msgid="316392039157473848">"允许应用程序在已安装的闹钟应用程序中设置闹钟。有些闹钟应用程序可能无法实现此功能。"</string> + <string name="permdesc_setAlarm" msgid="316392039157473848">"允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"</string> <string name="permlab_addVoicemail" msgid="5525660026090959044">"添加语音邮件"</string> - <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允许应用程序向您的语音信箱收件箱添加邮件。"</string> + <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允许应用向您的语音信箱收件箱添加邮件。"</string> <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改“浏览器”地理位置的权限"</string> - <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允许应用程序修改“浏览器”的地理位置权限。恶意应用程序可能借此向任意网站发送位置信息。"</string> + <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"</string> <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"验证软件包"</string> - <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允许应用程序验证程序包是否可以安装。"</string> + <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允许应用验证程序包是否可以安装。"</string> <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"绑定到软件包验证程序"</string> - <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允许用户请求使用程序包验证程序。普通应用程序绝不需要此权限。"</string> + <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允许用户请求使用程序包验证程序。普通应用绝不需要此权限。"</string> <string name="permlab_serialPort" msgid="546083327654631076">"访问串行端口"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"允许持有人使用 SerialManager API 访问串行端口。"</string> <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string> @@ -918,12 +918,12 @@ <string name="loading" msgid="7933681260296021180">"正在加载..."</string> <string name="capital_on" msgid="1544682755514494298">"打开"</string> <string name="capital_off" msgid="6815870386972805832">"关闭"</string> - <string name="whichApplication" msgid="4533185947064773386">"选择要使用的应用程序:"</string> + <string name="whichApplication" msgid="4533185947064773386">"选择要使用的应用:"</string> <string name="alwaysUse" msgid="4583018368000610438">"设为默认选项。"</string> - <string name="clearDefaultHintMsg" msgid="3252584689512077257">"在“系统设置”>“应用程序”>“已下载”中清除默认设置。"</string> + <string name="clearDefaultHintMsg" msgid="3252584689512077257">"在“系统设置”>“应用”>“已下载”中清除默认设置。"</string> <string name="chooseActivity" msgid="7486876147751803333">"选择操作"</string> - <string name="chooseUsbActivity" msgid="6894748416073583509">"为 USB 设备选择一个应用程序"</string> - <string name="noApplications" msgid="2991814273936504689">"没有应用程序可执行此操作。"</string> + <string name="chooseUsbActivity" msgid="6894748416073583509">"为 USB 设备选择一个应用"</string> + <string name="noApplications" msgid="2991814273936504689">"没有应用可执行此操作。"</string> <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"很抱歉,“<xliff:g id="APPLICATION">%1$s</xliff:g>”已停止运行。"</string> <string name="aerr_process" msgid="4507058997035697579">"抱歉,进程“<xliff:g id="PROCESS">%1$s</xliff:g>”已停止运行。"</string> @@ -936,26 +936,26 @@ <string name="report" msgid="4060218260984795706">"报告"</string> <string name="wait" msgid="7147118217226317732">"等待"</string> <string name="webpage_unresponsive" msgid="3272758351138122503">"该网页已无响应。"\n\n"要将其关闭吗?"</string> - <string name="launch_warning_title" msgid="1547997780506713581">"应用程序已重定向"</string> + <string name="launch_warning_title" msgid="1547997780506713581">"应用已重定向"</string> <string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前正在运行。"</string> <string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g>已启动。"</string> <string name="screen_compat_mode_scale" msgid="3202955667675944499">"缩放"</string> <string name="screen_compat_mode_show" msgid="4013878876486655892">"始终显示"</string> - <string name="screen_compat_mode_hint" msgid="1064524084543304459">"在“系统设置”>“应用程序”>“已下载”中重新启用此模式。"</string> - <string name="smv_application" msgid="3307209192155442829">"“<xliff:g id="APPLICATION">%1$s</xliff:g>”应用程序(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string> + <string name="screen_compat_mode_hint" msgid="1064524084543304459">"在“系统设置”>“应用”>“已下载”中重新启用此模式。"</string> + <string name="smv_application" msgid="3307209192155442829">"“<xliff:g id="APPLICATION">%1$s</xliff:g>”应用(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string> <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string> <string name="android_upgrading_title" msgid="1584192285441405746">"Android 正在升级..."</string> <string name="android_upgrading_apk" msgid="7904042682111526169">"正在优化第 <xliff:g id="NUMBER_0">%1$d</xliff:g> 个(共 <xliff:g id="NUMBER_1">%2$d</xliff:g> 个)。"</string> - <string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在启动应用程序。"</string> + <string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在启动应用。"</string> <string name="android_upgrading_complete" msgid="1405954754112999229">"即将完成启动。"</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string> - <string name="heavy_weight_notification_detail" msgid="1721681741617898865">"触摸可切换至应用程序"</string> - <string name="heavy_weight_switcher_title" msgid="7153167085403298169">"切换应用程序吗?"</string> - <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"已有一个应用程序正在运行,要启动新的应用程序,您必须先停止该应用程序。"</string> + <string name="heavy_weight_notification_detail" msgid="1721681741617898865">"触摸可切换至应用"</string> + <string name="heavy_weight_switcher_title" msgid="7153167085403298169">"切换应用吗?"</string> + <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"已有一个应用正在运行,要启动新的应用,您必须先停止该应用。"</string> <string name="old_app_action" msgid="493129172238566282">"返回至<xliff:g id="OLD_APP">%1$s</xliff:g>"</string> - <string name="old_app_description" msgid="2082094275580358049">"不启动新的应用程序。"</string> + <string name="old_app_description" msgid="2082094275580358049">"不启动新的应用。"</string> <string name="new_app_action" msgid="5472756926945440706">"启动<xliff:g id="OLD_APP">%1$s</xliff:g>"</string> - <string name="new_app_description" msgid="1932143598371537340">"停止旧的应用程序,但不保存。"</string> + <string name="new_app_description" msgid="1932143598371537340">"停止旧的应用,但不保存。"</string> <string name="sendText" msgid="5209874571959469142">"选择要对文字执行的操作"</string> <string name="volume_ringtone" msgid="6885421406845734650">"铃声音量"</string> <string name="volume_music" msgid="5421651157138628171">"媒体音量"</string> @@ -1003,7 +1003,7 @@ <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> <string name="select_character" msgid="3365550120617701745">"插入字符"</string> - <string name="sms_control_default_app_name" msgid="3058577482636640465">"未知应用程序"</string> + <string name="sms_control_default_app_name" msgid="3058577482636640465">"未知应用"</string> <string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string> <string name="sms_control_message" msgid="4073755190243093924">"正在发送大量短信。触摸“确定”继续,或触摸“取消”停止发送。"</string> <string name="sms_control_yes" msgid="2532062172402615953">"确定"</string> @@ -1039,7 +1039,7 @@ <string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"关闭 USB 存储设备"</string> <string name="usb_storage_stop_error_message" msgid="1970374898263063836">"关闭 USB 存储设备时遇到问题。请检查您是否已卸载了 USB 主设备,然后重试。"</string> <string name="dlg_confirm_kill_storage_users_title" msgid="963039033470478697">"打开 USB 存储设备"</string> - <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"如果您打开 USB 存储设备,您正在使用的某些应用程序将会停止,并且在您关闭 USB 存储设备前都将无法使用。"</string> + <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"如果您打开 USB 存储设备,您正在使用的某些应用将会停止,并且在您关闭 USB 存储设备前都将无法使用。"</string> <string name="dlg_error_title" msgid="7323658469626514207">"USB 操作失败"</string> <string name="dlg_ok" msgid="7376953167039865701">"确定"</string> <string name="usb_mtp_notification_title" msgid="3699913097391550394">"作为媒体设备连接"</string> @@ -1084,9 +1084,9 @@ <string name="ext_media_nomedia_notification_message" product="default" msgid="3870120652983659641">"SD 卡已移除。请插入新的 SD 卡。"</string> <string name="activity_list_empty" msgid="1675388330786841066">"未找到匹配的活动。"</string> <string name="permlab_pkgUsageStats" msgid="8787352074326748892">"更新组件使用情况统计"</string> - <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"允许应用程序修改收集到的组件使用情况统计信息。普通应用程序不能使用此权限。"</string> + <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"允许应用修改收集到的组件使用情况统计信息。普通应用不能使用此权限。"</string> <string name="permlab_copyProtectedData" msgid="4341036311211406692">"复制内容"</string> - <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用程序调用默认的容器服务,以便复制内容。普通应用程序不能使用此权限。"</string> + <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用调用默认的容器服务,以便复制内容。普通应用不能使用此权限。"</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"触摸两次可进行缩放控制"</string> <string name="gadget_host_error_inflating" msgid="4882004314906466162">"无法添加窗口小部件。"</string> <string name="ime_action_go" msgid="8320845651737369027">"开始"</string> @@ -1098,13 +1098,13 @@ <string name="ime_action_default" msgid="2840921885558045721">"执行"</string> <string name="dial_number_using" msgid="5789176425167573586">"拨打电话"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string> <string name="create_contact_using" msgid="4947405226788104538">"创建电话号码为"\n"<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string> - <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用程序请求获得相应权限,以便在当前和以后访问您的帐户。"</string> + <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐户。"</string> <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您是否同意此请求?"</string> <string name="grant_permissions_header_text" msgid="6874497408201826708">"访问权限请求"</string> <string name="allow" msgid="7225948811296386551">"允许"</string> <string name="deny" msgid="2081879885755434506">"拒绝"</string> <string name="permission_request_notification_title" msgid="6486759795926237907">"许可权限请求"</string> - <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用程序对帐户 <xliff:g id="ACCOUNT">%s</xliff:g>"\n" 提出权限请求。"</string> + <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用对帐户 <xliff:g id="ACCOUNT">%s</xliff:g>"\n" 提出权限请求。"</string> <string name="input_method_binding_label" msgid="1283557179944992649">"输入法"</string> <string name="sync_binding_label" msgid="3687969138375092423">"同步"</string> <string name="accessibility_binding_label" msgid="4148120742096474641">"辅助功能"</string> @@ -1197,7 +1197,7 @@ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"模式更改"</string> <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> - <string name="activitychooserview_choose_application" msgid="2125168057199941199">"选择应用程序"</string> + <string name="activitychooserview_choose_application" msgid="2125168057199941199">"选择应用"</string> <string name="shareactionprovider_share_with" msgid="806688056141131819">"共享对象"</string> <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"与“<xliff:g id="APPLICATION_NAME">%s</xliff:g>”共享"</string> <string name="content_description_sliding_handle" msgid="415975056159262248">"滑动手柄。触摸并按住。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index e81e393..447af8a 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -336,11 +336,11 @@ <string name="permdesc_writeContacts" product="tablet" msgid="988969759110632978">"允許應用程式修改平板電腦上儲存的聯絡人 (地址) 資料。請注意,惡意應用程式可能利用此功能清除或修改您的聯絡人資料。"</string> <string name="permdesc_writeContacts" product="default" msgid="5075164818647934067">"允許應用程式修改手機上儲存的聯絡人 (地址) 資料。請注意,惡意應用程式可能利用此功能清除或修改您的聯絡人資料。"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"讀取通話紀錄"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"允許應用程式讀取平板電腦的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能利用此功能將您的資料傳送給他人。"</string> - <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"允許應用程式讀取手機的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能利用此功能將您的資料傳送給他人。"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3995157599976515002">"允許應用程式讀取平板電腦的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能將您的資料傳送給他人。"</string> + <string name="permdesc_readCallLog" product="default" msgid="3452017559804750758">"允許應用程式讀取手機的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能將您的資料傳送給他人。"</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"寫入通話紀錄"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允許應用程式修改平板電腦的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能利用此功能刪除或修改您的通話記錄。"</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允許應用程式修改手機的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能利用此功能刪除或修改您的通話記錄。"</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"允許應用程式修改平板電腦的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改您的通話紀錄。"</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"允許應用程式修改手機的通話紀錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改您的通話紀錄。"</string> <string name="permlab_readProfile" msgid="6824681438529842282">"讀取您的個人資料"</string> <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"允許應用程式讀取裝置上儲存的個人資料,例如您的姓名和聯絡資訊。這表示應用程式可以識別您的身分,並將您的個人資料傳送給他人。"</string> <string name="permlab_writeProfile" msgid="4679878325177177400">"寫入您的個人資料"</string> @@ -743,8 +743,7 @@ <string name="double_tap_toast" msgid="4595046515400268881">"提示:輕按兩下即可縮放。"</string> <string name="autofill_this_form" msgid="4616758841157816676">"自動填入功能"</string> <string name="setup_autofill" msgid="7103495070180590814">"設定自動填入功能"</string> - <!-- no translation found for autofill_address_name_separator (6350145154779706772) --> - <skip /> + <string name="autofill_address_name_separator" msgid="6350145154779706772">" //*** Empty segment here as a separator ***//"</string> <string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <string name="autofill_address_summary_format" msgid="4874459455786827344">"$1$2$3"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index db908ca..428790f 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -589,12 +589,6 @@ <!-- NumberPicker style. --> <attr name="numberPickerStyle" format="reference" /> - <!-- NumberPicker up button style. --> - <attr name="numberPickerUpButtonStyle" format="reference" /> - <!-- NumberPicker down button style. --> - <attr name="numberPickerDownButtonStyle" format="reference" /> - <!-- NumberPicker input text style. --> - <attr name="numberPickerInputTextStyle" format="reference" /> <!-- The CalendarView style. --> <attr name="calendarViewStyle" format="reference" /> @@ -3617,12 +3611,12 @@ <declare-styleable name="NumberPicker"> <!-- @hide Color for the solid color background if such for optimized rendering. --> <attr name="solidColor" format="color|reference" /> - <!-- @hide Whether the number picker supports fligning. --> - <attr name="flingable" format="boolean" /> <!-- @hide The divider for making the selection area. --> <attr name="selectionDivider" format="reference" /> <!-- @hide The height of the selection divider. --> <attr name="selectionDividerHeight" format="dimension" /> + <!-- @hide The distance between the two selection dividers. --> + <attr name="selectionDividersDistance" format="dimension" /> <!-- @hide The min height of the NumberPicker. --> <attr name="internalMinHeight" format="dimension" /> <!-- @hide The max height of the NumberPicker. --> @@ -3631,6 +3625,10 @@ <attr name="internalMinWidth" format="dimension" /> <!-- @hide The max width of the NumberPicker. --> <attr name="internalMaxWidth" format="dimension" /> + <!-- @hide The layout of the number picker. --> + <attr name="internalLayout" /> + <!-- @hide The minimal move distance of a swipe to be considered a fling. --> + <attr name="minFlingDistance" format="dimension" /> </declare-styleable> <declare-styleable name="TimePicker"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index b1a4b42..5ff2c01 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -113,7 +113,7 @@ <color name="lockscreen_owner_info">#ff9a9a9a</color> <!-- FaceLock --> - <color name="facelock_color_background">#000000</color> + <color name="facelock_spotlight_mask">#CC000000</color> <!-- For holo theme --> <drawable name="screen_background_holo_light">#fff3f3f3</drawable> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4d9b043..4fde018 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -284,6 +284,10 @@ point on the move. A value of 0 means no periodic scans will be used in the framework. --> <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer> + <!-- Wifi driver stop delay, in milliseconds. + Default value is 2 minutes. --> + <integer translatable="false" name="config_wifi_driver_stop_delay">120000</integer> + <!-- Flag indicating whether the keyguard should be bypassed when the slider is open. This can be set or unset depending how easily the slider can be opened (for example, in a pocket or purse). --> @@ -304,6 +308,9 @@ <!-- If this is true, the screen will fade off. --> <bool name="config_animateScreenLights">true</bool> + <!-- If this is true, key chords can be used to take a screenshot on the device. --> + <bool name="config_enableScreenshotChord">true</bool> + <!-- If true, the screen can be rotated via the accelerometer in all 4 rotations as the default behavior. --> <bool name="config_allowAllRotations">false</bool> @@ -628,6 +635,10 @@ cell broadcasting sms, and MMS. --> <bool name="config_sms_capable">true</bool> + <!-- Enable/disable default bluetooth profiles: + HSP_AG, ObexObjectPush, Audio, NAP --> + <bool name="config_bluetooth_default_profiles">true</bool> + <!-- IP address of the dns server to use if nobody else suggests one --> <string name="config_default_dns_server" translatable="false">8.8.8.8</string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index ea1a70a..e1bc33b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -194,6 +194,10 @@ <java-symbol type="id" name="zoomIn" /> <java-symbol type="id" name="zoomMagnify" /> <java-symbol type="id" name="zoomOut" /> + <java-symbol type="id" name="actions" /> + <java-symbol type="id" name="action0" /> + <java-symbol type="id" name="action1" /> + <java-symbol type="id" name="action2" /> <java-symbol type="attr" name="actionModeShareDrawable" /> <java-symbol type="attr" name="alertDialogCenterButtons" /> @@ -240,6 +244,8 @@ <java-symbol type="bool" name="config_useMasterVolume" /> <java-symbol type="bool" name="config_enableWallpaperService" /> <java-symbol type="bool" name="config_sendAudioBecomingNoisy" /> + <java-symbol type="bool" name="config_enableScreenshotChord" /> + <java-symbol type="bool" name="config_bluetooth_default_profiles" /> <java-symbol type="integer" name="config_cursorWindowSize" /> <java-symbol type="integer" name="config_longPressOnPowerBehavior" /> @@ -251,6 +257,7 @@ <java-symbol type="integer" name="db_journal_size_limit" /> <java-symbol type="integer" name="db_wal_autocheckpoint" /> <java-symbol type="integer" name="max_action_buttons" /> + <java-symbol type="integer" name="config_wifi_driver_stop_delay" /> <java-symbol type="color" name="tab_indicator_text_v4" /> @@ -1062,6 +1069,7 @@ <java-symbol type="layout" name="zoom_container" /> <java-symbol type="layout" name="zoom_controls" /> <java-symbol type="layout" name="zoom_magnify" /> + <java-symbol type="layout" name="notification_intruder_content" /> <java-symbol type="anim" name="slide_in_child_bottom" /> <java-symbol type="anim" name="slide_in_right" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index a51f3f9..288b8b2 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -521,10 +521,10 @@ please see styles_device_defaults.xml. </style> <style name="Widget.NumberPicker"> + <item name="android:internalLayout">@android:layout/number_picker</item> <item name="android:orientation">vertical</item> <item name="android:fadingEdge">vertical</item> <item name="android:fadingEdgeLength">50dip</item> - <item name="android:flingable">false</item> </style> <style name="Widget.TimePicker"> @@ -536,25 +536,6 @@ please see styles_device_defaults.xml. <item name="android:calendarViewShown">false</item> </style> - <style name="Widget.ImageButton.NumberPickerUpButton"> - <item name="android:background">@android:drawable/numberpicker_up_btn</item> - <item name="android:paddingTop">22dip</item> - <item name="android:paddingBottom">22dip</item> - </style> - - <style name="Widget.ImageButton.NumberPickerDownButton"> - <item name="android:background">@android:drawable/numberpicker_down_btn</item> - <item name="android:paddingTop">22dip</item> - <item name="android:paddingBottom">22dip</item> - </style> - - <style name="Widget.EditText.NumberPickerInputText"> - <item name="android:textAppearance">@style/TextAppearance.Large.Inverse.NumberPickerInputText</item> - <item name="android:gravity">center</item> - <item name="android:singleLine">true</item> - <item name="android:background">@drawable/numberpicker_input</item> - </style> - <style name="Widget.AutoCompleteTextView" parent="Widget.EditText"> <item name="android:completionHintView">@android:layout/simple_dropdown_hint</item> <item name="android:completionThreshold">2</item> @@ -1656,12 +1637,14 @@ please see styles_device_defaults.xml. </style> <style name="Widget.Holo.NumberPicker" parent="Widget.NumberPicker"> + <item name="android:internalLayout">@android:layout/number_picker_with_selector_wheel</item> <item name="android:solidColor">@android:color/transparent</item> - <item name="android:flingable">true</item> <item name="android:selectionDivider">@android:drawable/numberpicker_selection_divider</item> <item name="android:selectionDividerHeight">2dip</item> + <item name="android:selectionDividersDistance">48dip</item> <item name="android:internalMinWidth">48dip</item> <item name="android:internalMaxHeight">200dip</item> + <item name="android:minFlingDistance">150dip</item> </style> <style name="Widget.Holo.TimePicker" parent="Widget.TimePicker"> @@ -1673,31 +1656,6 @@ please see styles_device_defaults.xml. <item name="android:calendarViewShown">true</item> </style> - <style name="Widget.Holo.ImageButton.NumberPickerUpButton"> - <item name="android:background">@null</item> - <item name="android:src">@android:drawable/numberpicker_up_btn_holo_dark</item> - <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">22dip</item> - </style> - - <style name="Widget.Holo.ImageButton.NumberPickerDownButton"> - <item name="android:background">@null</item> - <item name="android:src">@android:drawable/numberpicker_down_btn_holo_dark</item> - <item name="android:paddingTop">22dip</item> - <item name="android:paddingBottom">16dip</item> - </style> - - <style name="Widget.Holo.EditText.NumberPickerInputText"> - <item name="android:paddingTop">13sp</item> - <item name="android:paddingBottom">13sp</item> - <item name="android:paddingLeft">2sp</item> - <item name="android:paddingRight">2sp</item> - <item name="android:gravity">center</item> - <item name="android:singleLine">true</item> - <item name="android:textSize">18sp</item> - <item name="android:background">@null</item> - </style> - <style name="Widget.Holo.ActivityChooserView" parent="Widget.ActivityChooserView"> </style> @@ -2109,17 +2067,6 @@ please see styles_device_defaults.xml. <style name="Widget.Holo.Light.DatePicker" parent="Widget.Holo.DatePicker"> </style> - <style name="Widget.Holo.Light.ImageButton.NumberPickerUpButton" parent="Widget.Holo.ImageButton.NumberPickerUpButton"> - <item name="android:src">@android:drawable/numberpicker_up_btn_holo_light</item> - </style> - - <style name="Widget.Holo.Light.ImageButton.NumberPickerDownButton" parent="Widget.Holo.ImageButton.NumberPickerDownButton"> - <item name="android:src">@android:drawable/numberpicker_down_btn_holo_light</item> - </style> - - <style name="Widget.Holo.Light.EditText.NumberPickerInputText" parent="Widget.Holo.EditText.NumberPickerInputText"> - </style> - <style name="Widget.Holo.Light.ActivityChooserView" parent="Widget.Holo.ActivityChooserView"> <item name="android:background">@android:drawable/ab_share_pack_holo_light</item> </style> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 6419872..330e68c 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -200,9 +200,6 @@ easier. <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Holo.CompoundButton.Switch"> </style> - <style name="Widget.DeviceDefault.EditText.NumberPickerInputText" parent="Widget.Holo.EditText.NumberPickerInputText"> - - </style> <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Holo.ExpandableListView.White"> </style> @@ -212,12 +209,6 @@ easier. <style name="Widget.DeviceDefault.GestureOverlayView" parent="Widget.Holo.GestureOverlayView"> </style> - <style name="Widget.DeviceDefault.ImageButton.NumberPickerDownButton" parent="Widget.Holo.ImageButton.NumberPickerDownButton"> - - </style> - <style name="Widget.DeviceDefault.ImageButton.NumberPickerUpButton" parent="Widget.Holo.ImageButton.NumberPickerUpButton"> - - </style> <style name="Widget.DeviceDefault.ImageWell" parent="Widget.Holo.ImageWell"> </style> @@ -465,9 +456,6 @@ easier. <style name="Widget.DeviceDefault.Light.DatePicker" parent="Widget.Holo.Light.DatePicker"> </style> - <style name="Widget.DeviceDefault.Light.EditText.NumberPickerInputText" parent="Widget.Holo.Light.EditText.NumberPickerInputText"> - - </style> <style name="Widget.DeviceDefault.Light.ExpandableListView.White" parent="Widget.Holo.Light.ExpandableListView.White"> </style> @@ -477,12 +465,6 @@ easier. <style name="Widget.DeviceDefault.Light.GestureOverlayView" parent="Widget.Holo.Light.GestureOverlayView"> </style> - <style name="Widget.DeviceDefault.Light.ImageButton.NumberPickerDownButton" parent="Widget.Holo.Light.ImageButton.NumberPickerDownButton"> - - </style> - <style name="Widget.DeviceDefault.Light.ImageButton.NumberPickerUpButton" parent="Widget.Holo.Light.ImageButton.NumberPickerUpButton"> - - </style> <style name="Widget.DeviceDefault.Light.ImageWell" parent="Widget.Holo.Light.ImageWell"> </style> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 55438b2..7e06e24 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -345,10 +345,7 @@ please see themes_device_defaults.xml. <!-- PreferenceFrameLayout attributes --> <item name="preferenceFrameLayoutStyle">@android:style/Widget.PreferenceFrameLayout</item> - <!-- NumberPicker styles--> - <item name="numberPickerUpButtonStyle">@style/Widget.ImageButton.NumberPickerUpButton</item> - <item name="numberPickerDownButtonStyle">@style/Widget.ImageButton.NumberPickerDownButton</item> - <item name="numberPickerInputTextStyle">@style/Widget.EditText.NumberPickerInputText</item> + <!-- NumberPicker style--> <item name="numberPickerStyle">@style/Widget.NumberPicker</item> <!-- CalendarView style--> @@ -1141,10 +1138,7 @@ please see themes_device_defaults.xml. <!-- PreferenceFrameLayout attributes --> <item name="preferenceFrameLayoutStyle">@android:style/Widget.Holo.PreferenceFrameLayout</item> - <!-- NumberPicker styles--> - <item name="numberPickerUpButtonStyle">@style/Widget.Holo.ImageButton.NumberPickerUpButton</item> - <item name="numberPickerDownButtonStyle">@style/Widget.Holo.ImageButton.NumberPickerDownButton</item> - <item name="numberPickerInputTextStyle">@style/Widget.Holo.EditText.NumberPickerInputText</item> + <!-- NumberPicker style--> <item name="numberPickerStyle">@style/Widget.Holo.NumberPicker</item> <!-- CalendarView style--> @@ -1443,10 +1437,7 @@ please see themes_device_defaults.xml. <item name="searchDialogTheme">@style/Theme.Holo.Light.SearchBar</item> - <!-- NumberPicker attributes and styles--> - <item name="numberPickerUpButtonStyle">@style/Widget.Holo.Light.ImageButton.NumberPickerUpButton</item> - <item name="numberPickerDownButtonStyle">@style/Widget.Holo.Light.ImageButton.NumberPickerDownButton</item> - <item name="numberPickerInputTextStyle">@style/Widget.Holo.Light.EditText.NumberPickerInputText</item> + <!-- NumberPicker style--> <item name="numberPickerStyle">@style/Widget.Holo.Light.NumberPicker</item> <!-- CalendarView style--> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 7fd981c..ae9255a 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -179,10 +179,7 @@ easier. <!-- PreferenceFrameLayout attributes --> <item name="preferenceFrameLayoutStyle">@android:style/Widget.DeviceDefault.PreferenceFrameLayout</item> - <!-- NumberPicker styles--> - <item name="numberPickerUpButtonStyle">@style/Widget.DeviceDefault.ImageButton.NumberPickerUpButton</item> - <item name="numberPickerDownButtonStyle">@style/Widget.DeviceDefault.ImageButton.NumberPickerDownButton</item> - <item name="numberPickerInputTextStyle">@style/Widget.DeviceDefault.EditText.NumberPickerInputText</item> + <!-- NumberPicker style--> <item name="numberPickerStyle">@style/Widget.DeviceDefault.NumberPicker</item> <!-- CalendarView style--> @@ -329,10 +326,7 @@ easier. <item name="searchDialogTheme">@style/Theme.DeviceDefault.Light.SearchBar</item> - <!-- NumberPicker attributes and styles--> - <item name="numberPickerUpButtonStyle">@style/Widget.DeviceDefault.Light.ImageButton.NumberPickerUpButton</item> - <item name="numberPickerDownButtonStyle">@style/Widget.DeviceDefault.Light.ImageButton.NumberPickerDownButton</item> - <item name="numberPickerInputTextStyle">@style/Widget.DeviceDefault.Light.EditText.NumberPickerInputText</item> + <!-- NumberPicker style --> <item name="numberPickerStyle">@style/Widget.DeviceDefault.Light.NumberPicker</item> <!-- CalendarView style--> diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java index 2982816..0c31e2d 100755 --- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; +import android.os.UserId; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; @@ -674,7 +675,7 @@ public class AppCacheTest extends AndroidTestCase { PackageDataObserver observer = new PackageDataObserver(); //wait on observer synchronized(observer) { - getPm().clearApplicationUserData(packageName, observer); + getPm().clearApplicationUserData(packageName, observer, 0 /* TODO: Other users */); long waitTime = 0; while(!observer.isDone() || (waitTime > MAX_WAIT_TIME)) { observer.wait(WAIT_TIME_INCR); @@ -717,7 +718,8 @@ public class AppCacheTest extends AndroidTestCase { File getDataDir() { try { - ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0); + ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0, + UserId.myUserId()); return new File(appInfo.dataDir); } catch (RemoteException e) { throw new RuntimeException("Pacakge manager dead", e); @@ -746,7 +748,7 @@ public class AppCacheTest extends AndroidTestCase { @LargeTest public void testClearApplicationUserDataNoObserver() throws Exception { - getPm().clearApplicationUserData(mContext.getPackageName(), null); + getPm().clearApplicationUserData(mContext.getPackageName(), null, UserId.myUserId()); //sleep for 1 minute Thread.sleep(60*1000); //confirm files dont exist diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java index 810ed0d..c573498 100644 --- a/core/tests/coretests/src/android/net/SSLTest.java +++ b/core/tests/coretests/src/android/net/SSLTest.java @@ -16,17 +16,16 @@ package android.net; -import android.net.SSLCertificateSocketFactory; import android.test.suitebuilder.annotation.Suppress; -import junit.framework.TestCase; - import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.Arrays; +import junit.framework.TestCase; -//This test relies on network resources. -@Suppress public class SSLTest extends TestCase { + //This test relies on network resources. + @Suppress public void testCertificate() throws Exception { // test www.fortify.net/sslcheck.html Socket ssl = SSLCertificateSocketFactory.getDefault().createSocket("www.fortify.net",443); @@ -49,4 +48,35 @@ public class SSLTest extends TestCase { // System.out.println(new String(b)); } + + public void testStringsToNpnBytes() { + byte[] expected = { + 6, 's', 'p', 'd', 'y', '/', '2', + 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + }; + assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList( + new byte[] { 's', 'p', 'd', 'y', '/', '2' }, + new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' }))); + } + + public void testStringsToNpnBytesEmptyByteArray() { + try { + SSLCertificateSocketFactory.toNpnProtocolsList(new byte[0]); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testStringsToNpnBytesEmptyArray() { + byte[] expected = {}; + assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList())); + } + + public void testStringsToNpnBytesOversizedInput() { + try { + SSLCertificateSocketFactory.toNpnProtocolsList(new byte[256]); + fail(); + } catch (IllegalArgumentException expected) { + } + } } |
