diff options
Diffstat (limited to 'core')
87 files changed, 4226 insertions, 545 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 0f65454..56462ae 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -943,7 +943,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM b = data.readStrongBinder(); IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b); int userId = data.readInt(); - boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId); + String abiOverride = data.readString(); + boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId, + abiOverride); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -3339,7 +3341,8 @@ class ActivityManagerProxy implements IActivityManager public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException { + IUiAutomationConnection connection, int userId, String instructionSet) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -3350,6 +3353,7 @@ class ActivityManagerProxy implements IActivityManager data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); data.writeStrongBinder(connection != null ? connection.asBinder() : null); data.writeInt(userId); + data.writeString(instructionSet); mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 2f35160..84673d9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1455,10 +1455,10 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest) { + public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId) { try { - mPM.addForwardingIntentFilter(filter, removable, userIdOrig, userIdDest); + mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); } catch (RemoteException e) { // Should never happen! } @@ -1468,14 +1468,31 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void clearForwardingIntentFilters(int userIdOrig) { + public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, + int targetUserId) { + addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); + } + + /** + * @hide + */ + @Override + public void clearCrossProfileIntentFilters(int sourceUserId) { try { - mPM.clearForwardingIntentFilters(userIdOrig); + mPM.clearCrossProfileIntentFilters(sourceUserId); } catch (RemoteException e) { // Should never happen! } } + /** + * @hide + */ + @Override + public void clearForwardingIntentFilters(int sourceUserId) { + clearCrossProfileIntentFilters(sourceUserId); + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ff8688d..1bd5abf 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -58,7 +58,9 @@ import android.hardware.ISerialManager; import android.hardware.SerialManager; import android.hardware.SystemSensorManager; import android.hardware.hdmi.HdmiCecManager; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiCecService; +import android.hardware.hdmi.IHdmiControlService; import android.hardware.camera2.CameraManager; import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; @@ -386,6 +388,11 @@ class ContextImpl extends Context { return new HdmiCecManager(IHdmiCecService.Stub.asInterface(b)); }}); + registerService(HDMI_CONTROL_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(HDMI_CONTROL_SERVICE); + return new HdmiControlManager(IHdmiControlService.Stub.asInterface(b)); + }}); registerService(CLIPBOARD_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { @@ -1734,7 +1741,8 @@ class ContextImpl extends Context { arguments.setAllowFds(false); } return ActivityManagerNative.getDefault().startInstrumentation( - className, profileFile, 0, arguments, null, null, getUserId()); + className, profileFile, 0, arguments, null, null, getUserId(), + null /* ABI override */); } catch (RemoteException e) { // System has crashed, nothing we can do. } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8434c2a..bf2d7e5 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -176,7 +176,8 @@ public interface IActivityManager extends IInterface { public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException; + IUiAutomationConnection connection, int userId, + String abiOverride) throws RemoteException; public void finishInstrumentation(IApplicationThread target, int resultCode, Bundle results) throws RemoteException; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 77b1acf..6b486c4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -176,15 +176,16 @@ public class DevicePolicyManager { public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; /** - * Flag for {@link #addForwardingIntentFilter}: the intents will forwarded to the primary user. + * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a + * managed profile to its parent. */ - public static int FLAG_TO_PRIMARY_USER = 0x0001; + public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001; /** - * Flag for {@link #addForwardingIntentFilter}: the intents will be forwarded to the managed - * profile. + * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the + * parent to its managed profile. */ - public static int FLAG_TO_MANAGED_PROFILE = 0x0002; + public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** * Return true if the given administrator component is currently @@ -1951,17 +1952,16 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to forward intents sent from the managed profile to the owner, or - * from the owner to the managed profile. - * If an intent matches this intent filter, then activities belonging to the other user can - * respond to this intent. + * Called by the profile owner so that some intents sent in the managed profile can also be + * resolved in the parent, or vice versa. * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param filter if an intent matches this IntentFilter, then it can be forwarded. + * @param filter The {@link IntentFilter} the intent has to match to be also resolved in the + * other profile */ - public void addForwardingIntentFilter(ComponentName admin, IntentFilter filter, int flags) { + public void addCrossProfileIntentFilter(ComponentName admin, IntentFilter filter, int flags) { if (mService != null) { try { - mService.addForwardingIntentFilter(admin, filter, flags); + mService.addCrossProfileIntentFilter(admin, filter, flags); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1969,14 +1969,14 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to remove the forwarding intent filters from the current user - * and from the owner. + * Called by a profile owner to remove the cross-profile intent filters from the managed profile + * and from the parent. * @param admin Which {@link DeviceAdminReceiver} this request is associated with. */ - public void clearForwardingIntentFilters(ComponentName admin) { + public void clearCrossProfileIntentFilters(ComponentName admin) { if (mService != null) { try { - mService.clearForwardingIntentFilters(admin); + mService.clearCrossProfileIntentFilters(admin); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3c08c14..cc6d715 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -122,8 +122,8 @@ interface IDevicePolicyManager { Bundle getApplicationRestrictions(in ComponentName who, in String packageName); void setUserRestriction(in ComponentName who, in String key, boolean enable); - void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); - void clearForwardingIntentFilters(in ComponentName admin); + void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); + void clearCrossProfileIntentFilters(in ComponentName admin); boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked); int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java new file mode 100644 index 0000000..da5cb10 --- /dev/null +++ b/core/java/android/app/backup/BackupTransport.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.backup; + +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; + +import com.android.internal.backup.BackupConstants; +import com.android.internal.backup.IBackupTransport; + +/** + * Concrete class that provides a stable-API bridge between IBackupTransport + * and its implementations. + * + * @hide + */ +public class BackupTransport { + IBackupTransport mBinderImpl = new TransportImpl(); + /** @hide */ + public IBinder getBinder() { + return mBinderImpl.asBinder(); + } + + // ------------------------------------------------------------------------------------ + // Transport self-description and general configuration interfaces + // + + /** + * Ask the transport for the name under which it should be registered. This will + * typically be its host service's component name, but need not be. + */ + public String name() { + throw new UnsupportedOperationException("Transport name() not implemented"); + } + + /** + * Ask the transport for an Intent that can be used to launch any internal + * configuration Activity that it wishes to present. For example, the transport + * may offer a UI for allowing the user to supply login credentials for the + * transport's off-device backend. + * + * If the transport does not supply any user-facing configuration UI, it should + * return null from this method. + * + * @return An Intent that can be passed to Context.startActivity() in order to + * launch the transport's configuration UI. This method will return null + * if the transport does not offer any user-facing configuration UI. + */ + public Intent configurationIntent() { + return null; + } + + /** + * On demand, supply a one-line string that can be shown to the user that + * describes the current backend destination. For example, a transport that + * can potentially associate backup data with arbitrary user accounts should + * include the name of the currently-active account here. + * + * @return A string describing the destination to which the transport is currently + * sending data. This method should not return null. + */ + public String currentDestinationString() { + throw new UnsupportedOperationException( + "Transport currentDestinationString() not implemented"); + } + + /** + * Ask the transport where, on local device storage, to keep backup state blobs. + * This is per-transport so that mock transports used for testing can coexist with + * "live" backup services without interfering with the live bookkeeping. The + * returned string should be a name that is expected to be unambiguous among all + * available backup transports; the name of the class implementing the transport + * is a good choice. + * + * @return A unique name, suitable for use as a file or directory name, that the + * Backup Manager could use to disambiguate state files associated with + * different backup transports. + */ + public String transportDirName() { + throw new UnsupportedOperationException( + "Transport transportDirName() not implemented"); + } + + // ------------------------------------------------------------------------------------ + // Key/value incremental backup support interfaces + + /** + * Verify that this is a suitable time for a backup pass. This should return zero + * if a backup is reasonable right now, some positive value otherwise. This method + * will be called outside of the {@link #performBackup}/{@link #finishBackup} pair. + * + * <p>If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + */ + public long requestBackupTime() { + return 0; + } + + /** + * Initialize the server side storage for this device, erasing all stored data. + * The transport may send the request immediately, or may buffer it. After + * this is called, {@link #finishBackup} will be called to ensure the request + * is sent and received successfully. + * + * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or + * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure). + */ + public int initializeDevice() { + return BackupConstants.TRANSPORT_ERROR; + } + + /** + * Send one application's key/value data update to the backup destination. The + * transport may send the data immediately, or may buffer it. After this is called, + * {@link #finishBackup} will be called to ensure the data is sent and recorded successfully. + * + * @param packageInfo The identity of the application whose data is being backed up. + * This specifically includes the signature list for the package. + * @param data The data stream that resulted from invoking the application's + * BackupService.doBackup() method. This may be a pipe rather than a file on + * persistent media, so it may not be seekable. + * @param wipeAllFirst When true, <i>all</i> backed-up data for the current device/account + * must be erased prior to the storage of the data provided here. The purpose of this + * is to provide a guarantee that no stale data exists in the restore set when the + * device begins providing incremental backups. + * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far), + * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or + * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has + * become lost due to inactivity purge or some other reason and needs re-initializing) + */ + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) { + return BackupConstants.TRANSPORT_ERROR; + } + + /** + * Erase the give application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. + * + * @return the same error codes as {@link #performBackup}. + */ + public int clearBackupData(PackageInfo packageInfo) { + return BackupConstants.TRANSPORT_ERROR; + } + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup} or {@link clearBackupData} to ensure that + * all data is sent. Only when this method returns true can a backup be assumed + * to have succeeded. + * + * @return the same error codes as {@link #performBackup}. + */ + public int finishBackup() { + return BackupConstants.TRANSPORT_ERROR; + } + + // ------------------------------------------------------------------------------------ + // Key/value dataset restore interfaces + + /** + * Get the set of all backups currently available over this transport. + * + * @return Descriptions of the set of restore images available for this device, + * or null if an error occurred (the attempt should be rescheduled). + **/ + public RestoreSet[] getAvailableRestoreSets() { + return null; + } + + /** + * Get the identifying token of the backup set currently being stored from + * this device. This is used in the case of applications wishing to restore + * their last-known-good data. + * + * @return A token that can be passed to {@link #startRestore}, or 0 if there + * is no backup set available corresponding to the current device state. + */ + public long getCurrentRestoreSet() { + return 0; + } + + /** + * Start restoring application data from backup. After calling this function, + * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData} + * to walk through the actual application data. + * + * @param token A backup token as returned by {@link #getAvailableRestoreSets} + * or {@link #getCurrentRestoreSet}. + * @param packages List of applications to restore (if data is available). + * Application data will be restored in the order given. + * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call + * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR} + * (an error occurred, the restore should be aborted and rescheduled). + */ + public int startRestore(long token, PackageInfo[] packages) { + return BackupConstants.TRANSPORT_ERROR; + } + + /** + * Get the package name of the next application with data in the backup store. + * + * @return The name of one of the packages supplied to {@link #startRestore}, + * or "" (the empty string) if no more backup data is available, + * or null if an error occurred (the restore should be aborted and rescheduled). + */ + public String nextRestorePackage() { + return null; + } + + /** + * Get the data for the application returned by {@link #nextRestorePackage}. + * @param data An open, writable file into which the backup data should be stored. + * @return the same error codes as {@link #startRestore}. + */ + public int getRestoreData(ParcelFileDescriptor outFd) { + return BackupConstants.TRANSPORT_ERROR; + } + + /** + * End a restore session (aborting any in-process data transfer as necessary), + * freeing any resources and connections used during the restore process. + */ + public void finishRestore() { + throw new UnsupportedOperationException( + "Transport finishRestore() not implemented"); + } + + /** + * Bridge between the actual IBackupTransport implementation and the stable API. If the + * binder interface needs to change, we use this layer to translate so that we can + * (if appropriate) decouple those framework-side changes from the BackupTransport + * implementations. + */ + class TransportImpl extends IBackupTransport.Stub { + + @Override + public String name() throws RemoteException { + return BackupTransport.this.name(); + } + + @Override + public Intent configurationIntent() throws RemoteException { + return BackupTransport.this.configurationIntent(); + } + + @Override + public String currentDestinationString() throws RemoteException { + return BackupTransport.this.currentDestinationString(); + } + + @Override + public String transportDirName() throws RemoteException { + return BackupTransport.this.transportDirName(); + } + + @Override + public long requestBackupTime() throws RemoteException { + return BackupTransport.this.requestBackupTime(); + } + + @Override + public int initializeDevice() throws RemoteException { + return BackupTransport.this.initializeDevice(); + } + + @Override + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) + throws RemoteException { + return BackupTransport.this.performBackup(packageInfo, inFd); + } + + @Override + public int clearBackupData(PackageInfo packageInfo) throws RemoteException { + return BackupTransport.this.clearBackupData(packageInfo); + } + + @Override + public int finishBackup() throws RemoteException { + return BackupTransport.this.finishBackup(); + } + + @Override + public RestoreSet[] getAvailableRestoreSets() throws RemoteException { + return BackupTransport.this.getAvailableRestoreSets(); + } + + @Override + public long getCurrentRestoreSet() throws RemoteException { + return BackupTransport.this.getCurrentRestoreSet(); + } + + @Override + public int startRestore(long token, PackageInfo[] packages) throws RemoteException { + return BackupTransport.this.startRestore(token, packages); + } + + @Override + public String nextRestorePackage() throws RemoteException { + return BackupTransport.this.nextRestorePackage(); + } + + @Override + public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException { + return BackupTransport.this.getRestoreData(outFd); + } + + @Override + public void finishRestore() throws RemoteException { + BackupTransport.this.finishRestore(); + } + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1574090..d898060 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -104,6 +104,12 @@ public interface BluetoothProfile { public static final int MAP = 9; /** + * A2DP Sink Profile + * @hide + */ + public static final int A2DP_SINK = 10; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index 6b4404d..46c9234 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -355,7 +355,14 @@ public interface SharedPreferences { /** * Registers a callback to be invoked when a change happens to a preference. - * + * + * <p class="caution"><strong>Caution:</strong> The preference manager does + * not currently store a strong reference to the listener. You must store a + * strong reference to the listener, or it will be susceptible to garbage + * collection. We recommend you keep a reference to the listener in the + * instance data of an object that will exist as long as you need the + * listener.</p> + * * @param listener The callback that will run. * @see #unregisterOnSharedPreferenceChangeListener */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6cb781f..44a6a5d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -112,7 +112,7 @@ interface IPackageManager { ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId); - boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest); + boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId); List<ResolveInfo> queryIntentActivities(in Intent intent, String resolvedType, int flags, int userId); @@ -248,10 +248,10 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); - void addForwardingIntentFilter(in IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest); + void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId, + int targetUserId); - void clearForwardingIntentFilters(int userIdOrig); + void clearCrossProfileIntentFilters(int sourceUserId); /** * Report the set of 'Home' activity candidates, plus (if any) which of them diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d7bd473..4672015 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -19,9 +19,12 @@ package android.content.pm; import android.app.PackageInstallObserver; import android.app.PackageUninstallObserver; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.FileBridge; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import java.io.OutputStream; + /** {@hide} */ public class PackageInstaller { private final PackageManager mPm; @@ -127,10 +130,17 @@ public class PackageInstaller { } } - public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes, - long lengthBytes) { + /** + * Open an APK file for writing, starting at the given offset. You can + * then stream data into the file, periodically calling + * {@link OutputStream#flush()} to ensure bytes have been written to + * disk. + */ + public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) { try { - return mSession.openWrite(overlayName, offsetBytes, lengthBytes); + final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName, + offsetBytes, lengthBytes); + return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 35bcc02..c5cd5c9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3576,24 +3576,38 @@ public abstract class PackageManager { } /** - * Adds a forwarding intent filter. After calling this method all intents sent from the user - * with id userIdOrig can also be be resolved by activities in the user with id userIdDest if - * they match the specified intent filter. - * @param filter the {@link IntentFilter} the intent has to match to be forwarded - * @param removable if set to false, {@link clearForwardingIntents} will not remove this intent - * filter - * @param userIdOrig user from which the intent can be forwarded - * @param userIdDest user to which the intent can be forwarded + * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the + * user with id sourceUserId can also be be resolved by activities in the user with id + * targetUserId if they match the specified intent filter. + * @param filter the {@link IntentFilter} the intent has to match + * @param removable if set to false, {@link clearCrossProfileIntentFilters} will not remove this + * {@link CrossProfileIntentFilter} * @hide */ + public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId); + + /** + * @hide + * @deprecated + * TODO: remove it as soon as the code of ManagedProvisionning is updated + */ public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable, - int userIdOrig, int userIdDest); + int sourceUserId, int targetUserId); /** - * Clearing all removable {@link ForwardingIntentFilter}s that are set with the given user as - * the origin. - * @param userIdOrig user from which the intent can be forwarded + * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their + * source + * @param sourceUserId + * be cleared. * @hide */ - public abstract void clearForwardingIntentFilters(int userIdOrig); + public abstract void clearCrossProfileIntentFilters(int sourceUserId); + + /** + * @hide + * @deprecated + * TODO: remove it as soon as the code of ManagedProvisionning is updated + */ + public abstract void clearForwardingIntentFilters(int sourceUserId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 1c838c3..ab8bf61 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2174,7 +2174,6 @@ public class PackageParser { } final int innerDepth = parser.getDepth(); - int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { @@ -2548,13 +2547,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_singleUser, false)) { a.info.flags |= ActivityInfo.FLAG_SINGLE_USER; - if (a.info.exported) { + if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Activity exported request ignored due to singleUser: " + a.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); a.info.exported = false; + setExported = true; } - setExported = true; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly, @@ -2907,7 +2906,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_singleUser, false)) { p.info.flags |= ProviderInfo.FLAG_SINGLE_USER; - if (p.info.exported) { + if (p.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Provider exported request ignored due to singleUser: " + p.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); @@ -3181,13 +3180,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_singleUser, false)) { s.info.flags |= ServiceInfo.FLAG_SINGLE_USER; - if (s.info.exported) { + if (s.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Service exported request ignored due to singleUser: " + s.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); s.info.exported = false; + setExported = true; } - setExported = true; } sa.recycle(); diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 86208fc..c8de2f1 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -142,9 +142,10 @@ public final class Sensor { public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature"; /** - * A constant describing a proximity sensor type. + * A constant describing a proximity sensor type. This is a wake up sensor. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. + * @see #isWakeUpSensor() */ public static final int TYPE_PROXIMITY = 8; @@ -307,8 +308,10 @@ public final class Sensor { * itself. The sensor continues to operate while the device is asleep * and will automatically wake the device to notify when significant * motion is detected. The application does not need to hold any wake - * locks for this sensor to trigger. + * locks for this sensor to trigger. This is a wake up sensor. * <p>See {@link TriggerEvent} for more details. + * + * @see #isWakeUpSensor() */ public static final int TYPE_SIGNIFICANT_MOTION = 17; @@ -381,11 +384,17 @@ public final class Sensor { /** * A constant describing a heart rate monitor. * <p> - * A sensor that measures the heart rate in beats per minute. + * The reported value is the heart rate in beats per minute. + * <p> + * The reported accuracy represents the status of the monitor during the reading. See the + * {@code SENSOR_STATUS_*} constants in {@link android.hardware.SensorManager SensorManager} + * for more details on accuracy/status values. In particular, when the accuracy is + * {@code SENSOR_STATUS_UNRELIABLE} or {@code SENSOR_STATUS_NO_CONTACT}, the heart rate + * value should be discarded. * <p> - * value[0] represents the beats per minute when the measurement was taken. - * value[0] is 0 if the heart rate monitor could not measure the rate or the - * rate is 0 beat per minute. + * This sensor requires permission {@code android.permission.BODY_SENSORS}. + * It will not be returned by {@code SensorManager.getSensorsList} nor + * {@code SensorManager.getDefaultSensor} if the application doesn't have this permission. */ public static final int TYPE_HEART_RATE = 21; @@ -397,6 +406,321 @@ public final class Sensor { public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; /** + * A non-wake up variant of proximity sensor. + * + * @see #TYPE_PROXIMITY + */ + public static final int TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = 22; + + /** + * A constant string describing a non-wake up proximity sensor type. + * + * @see #TYPE_NON_WAKE_UP_PROXIMITY_SENSOR + */ + public static final String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = + "android.sensor.non_wake_up_proximity_sensor"; + + /** + * A constant describing a wake up variant of accelerometer sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ACCELEROMETER + */ + public static final int TYPE_WAKE_UP_ACCELEROMETER = 23; + + /** + * A constant string describing a wake up accelerometer. + * + * @see #TYPE_WAKE_UP_ACCELEROMETER + */ + public static final String STRING_TYPE_WAKE_UP_ACCELEROMETER = + "android.sensor.wake_up_accelerometer"; + + /** + * A constant describing a wake up variant of a magnetic field sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_MAGNETIC_FIELD + */ + public static final int TYPE_WAKE_UP_MAGNETIC_FIELD = 24; + + /** + * A constant string describing a wake up magnetic field sensor. + * + * @see #TYPE_WAKE_UP_MAGNETIC_FIELD + */ + public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD = + "android.sensor.wake_up_magnetic_field"; + + /** + * A constant describing a wake up variant of an orientation sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ORIENTATION + */ + public static final int TYPE_WAKE_UP_ORIENTATION = 25; + + /** + * A constant string describing a wake up orientation sensor. + * + * @see #TYPE_WAKE_UP_ORIENTATION + */ + public static final String STRING_TYPE_WAKE_UP_ORIENTATION = + "android.sensor.wake_up_orientation"; + + /** + * A constant describing a wake up variant of a gyroscope sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GYROSCOPE + */ + public static final int TYPE_WAKE_UP_GYROSCOPE = 26; + + /** + * A constant string describing a wake up gyroscope sensor type. + * + * @see #TYPE_WAKE_UP_GYROSCOPE + */ + public static final String STRING_TYPE_WAKE_UP_GYROSCOPE = "android.sensor.wake_up_gyroscope"; + + /** + * A constant describing a wake up variant of a light sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_LIGHT + */ + public static final int TYPE_WAKE_UP_LIGHT = 27; + + /** + * A constant string describing a wake up light sensor type. + * + * @see #TYPE_WAKE_UP_LIGHT + */ + public static final String STRING_TYPE_WAKE_UP_LIGHT = "android.sensor.wake_up_light"; + + /** + * A constant describing a wake up variant of a pressure sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_PRESSURE + */ + public static final int TYPE_WAKE_UP_PRESSURE = 28; + + /** + * A constant string describing a wake up pressure sensor type. + * + * @see #TYPE_WAKE_UP_PRESSURE + */ + public static final String STRING_TYPE_WAKE_UP_PRESSURE = "android.sensor.wake_up_pressure"; + + /** + * A constant describing a wake up variant of a gravity sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GRAVITY + */ + public static final int TYPE_WAKE_UP_GRAVITY = 29; + + /** + * A constant string describing a wake up gravity sensor type. + * + * @see #TYPE_WAKE_UP_GRAVITY + */ + public static final String STRING_TYPE_WAKE_UP_GRAVITY = "android.sensor.wake_up_gravity"; + + /** + * A constant describing a wake up variant of a linear acceleration sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_LINEAR_ACCELERATION + */ + public static final int TYPE_WAKE_UP_LINEAR_ACCELERATION = 30; + + /** + * A constant string describing a wake up linear acceleration sensor type. + * + * @see #TYPE_WAKE_UP_LINEAR_ACCELERATION + */ + public static final String STRING_TYPE_WAKE_UP_LINEAR_ACCELERATION = + "android.sensor.wake_up_linear_acceleration"; + + /** + * A constant describing a wake up variant of a rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31; + + /** + * A constant string describing a wake up rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_ROTATION_VECTOR = + "android.sensor.wake_up_rotation_vector"; + + /** + * A constant describing a wake up variant of a relative humidity sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_RELATIVE_HUMIDITY + */ + public static final int TYPE_WAKE_UP_RELATIVE_HUMIDITY = 32; + + /** + * A constant string describing a wake up relative humidity sensor type. + * + * @see #TYPE_WAKE_UP_RELATIVE_HUMIDITY + */ + public static final String STRING_TYPE_WAKE_UP_RELATIVE_HUMIDITY = + "android.sensor.wake_up_relative_humidity"; + + /** + * A constant describing a wake up variant of an ambient temperature sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_AMBIENT_TEMPERATURE + */ + public static final int TYPE_WAKE_UP_AMBIENT_TEMPERATURE = 33; + + /** + * A constant string describing a wake up ambient temperature sensor type. + * + * @see #TYPE_WAKE_UP_AMBIENT_TEMPERATURE + */ + public static final String STRING_TYPE_WAKE_UP_AMBIENT_TEMPERATURE = + "android.sensor.wake_up_ambient_temperature"; + + /** + * A constant describing a wake up variant of an uncalibrated magnetic field sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED + */ + public static final int TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = 34; + + /** + * A constant string describing a wake up uncalibrated magnetic field sensor type. + * + * @see #TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED + */ + public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = + "android.sensor.wake_up_magnetic_field_uncalibrated"; + + /** + * A constant describing a wake up variant of a game rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GAME_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_GAME_ROTATION_VECTOR = 35; + + /** + * A constant string describing a wake up game rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_GAME_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_GAME_ROTATION_VECTOR = + "android.sensor.wake_up_game_rotation_vector"; + + /** + * A constant describing a wake up variant of an uncalibrated gyroscope sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GYROSCOPE_UNCALIBRATED + */ + public static final int TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = 36; + + /** + * A constant string describing a wake up uncalibrated gyroscope sensor type. + * + * @see #TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED + */ + public static final String STRING_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = + "android.sensor.wake_up_gyroscope_uncalibrated"; + + /** + * A constant describing a wake up variant of a step detector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_STEP_DETECTOR + */ + public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37; + + /** + * A constant string describing a wake up step detector sensor type. + * + * @see #TYPE_WAKE_UP_STEP_DETECTOR + */ + public static final String STRING_TYPE_WAKE_UP_STEP_DETECTOR = + "android.sensor.wake_up_step_detector"; + + /** + * A constant describing a wake up variant of a step counter sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_STEP_COUNTER + */ + public static final int TYPE_WAKE_UP_STEP_COUNTER = 38; + + /** + * A constant string describing a wake up step counter sensor type. + * + * @see #TYPE_WAKE_UP_STEP_COUNTER + */ + public static final String STRING_TYPE_WAKE_UP_STEP_COUNTER = + "android.sensor.wake_up_step_counter"; + + /** + * A constant describing a wake up variant of a geomagnetic rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = 39; + + /** + * A constant string describing a wake up geomagnetic rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = + "android.sensor.wake_up_geomagnetic_rotation_vector"; + + /** + * A constant describing a wake up variant of a heart rate sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_HEART_RATE + */ + public static final int TYPE_WAKE_UP_HEART_RATE = 40; + + /** + * A constant string describing a wake up heart rate sensor type. + * + * @see #TYPE_WAKE_UP_HEART_RATE + */ + public static final String STRING_TYPE_WAKE_UP_HEART_RATE = "android.sensor.wake_up_heart_rate"; + + /** + * A sensor of this type generates an event each time a tilt event is detected. A tilt event + * is generated if the direction of the 2-seconds window average gravity changed by at + * least 35 degrees since the activation of the sensor. It is a wake up sensor. + * + * @see #isWakeUpSensor() + */ + public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41; + + /** + * A constant string describing a wake up tilt detector sensor type. + * + * @see #TYPE_WAKE_UP_TILT_DETECTOR + */ + public static final String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR = + "android.sensor.wake_up_tilt_detector"; + + /** * A constant describing a wake gesture sensor. * <p> * Wake gesture sensors enable waking up the device based on a device specific motion. @@ -410,6 +734,7 @@ public final class Sensor { * the device. This sensor must be low power, as it is likely to be activated 24/7. * Values of events created by this sensors should not be used. * + * @see #isWakeUpSensor() * @hide This sensor is expected to only be used by the power manager */ public static final int TYPE_WAKE_GESTURE = 42; @@ -467,7 +792,29 @@ public final class Sensor { REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_DETECTOR REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_COUNTER REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR - REPORTING_MODE_ON_CHANGE, 1 // SENSOR_TYPE_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR + // wake up variants of base sensors + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ACCELEROMETER + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ORIENTATION + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GYROSCOPE + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_LIGHT + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_PRESSURE + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GRAVITY + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_LINEAR_ACCELERATION + REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_ROTATION_VECTOR + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_RELATIVE_HUMIDITY + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_AMBIENT_TEMPERATURE + REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED + REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_WAKE_UP_GAME_ROTATION_VECTOR + REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_DETECTOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_COUNTER + REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR + REPORTING_MODE_ONE_SHOT, 1, // SENSOR_TYPE_WAKE_GESTURE }; static int getReportingMode(Sensor sensor) { @@ -525,6 +872,8 @@ public final class Sensor { private int mFifoMaxEventCount; private String mStringType; private String mRequiredPermission; + private int mMaxDelay; + private boolean mWakeUpSensor; Sensor() { } @@ -613,6 +962,7 @@ public final class Sensor { } /** + * @hide * @return The permission required to access this sensor. If empty, no permission is required. */ public String getRequiredPermission() { @@ -624,6 +974,51 @@ public final class Sensor { return mHandle; } + /** + * This value is defined only for continuous mode sensors. It is the delay between two + * sensor events corresponding to the lowest frequency that this sensor supports. When + * lower frequencies are requested through registerListener() the events will be generated + * at this frequency instead. It can be used to estimate when the batch FIFO may be full. + * Older devices may set this value to zero. Ignore this value in case it is negative + * or zero. + * + * @return The max delay for this sensor in microseconds. + */ + public int getMaxDelay() { + return mMaxDelay; + } + + /** + * Returns whether this sensor is a wake-up sensor. + * <p> + * Wake up sensors wake the application processor up when they have events to deliver. When a + * wake up sensor is registered to without batching enabled, each event will wake the + * application processor up. + * <p> + * When a wake up sensor is registered to with batching enabled, it + * wakes the application processor up when maxReportingLatency has elapsed or when the hardware + * FIFO storing the events from wake up sensors is getting full. + * <p> + * Non-wake up sensors never wake the application processor up. Their events are only reported + * when the application processor is awake, for example because the application holds a wake + * lock, or another source woke the application processor up. + * <p> + * When a non-wake up sensor is registered to without batching enabled, the measurements made + * while the application processor is asleep might be lost and never returned. + * <p> + * When a non-wake up sensor is registered to with batching enabled, the measurements made while + * the application processor is asleep are stored in the hardware FIFO for non-wake up sensors. + * When this FIFO gets full, new events start overwriting older events. When the application + * then wakes up, the latest events are returned, and some old events might be lost. The number + * of events actually returned depends on the hardware FIFO size, as well as on what other + * sensors are activated. If losing sensor events is not acceptable during batching, you must + * use the wake-up version of the sensor. + * @return true if this is a wake up sensor, false otherwise. + */ + public boolean isWakeUpSensor() { + return mWakeUpSensor; + } + void setRange(float max, float res) { mMaxRange = max; mResolution = res; diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java index 677d244..0d859fb 100644 --- a/core/java/android/hardware/SensorEventListener.java +++ b/core/java/android/hardware/SensorEventListener.java @@ -39,11 +39,13 @@ public interface SensorEventListener { public void onSensorChanged(SensorEvent event); /** - * Called when the accuracy of a sensor has changed. - * <p>See {@link android.hardware.SensorManager SensorManager} - * for details. + * Called when the accuracy of the registered sensor has changed. + * + * <p>See the SENSOR_STATUS_* constants in + * {@link android.hardware.SensorManager SensorManager} for details. * - * @param accuracy The new accuracy of this sensor + * @param accuracy The new accuracy of this sensor, one of + * {@code SensorManager.SENSOR_STATUS_*} */ - public void onAccuracyChanged(Sensor sensor, int accuracy); + public void onAccuracyChanged(Sensor sensor, int accuracy); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 5f2b5f0..25c7630 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -321,6 +321,13 @@ public abstract class SensorManager { /** + * The values returned by this sensor cannot be trusted because the sensor + * had no contact with what it was measuring (for example, the heart rate + * monitor is not in contact with the user). + */ + public static final int SENSOR_STATUS_NO_CONTACT = -1; + + /** * The values returned by this sensor cannot be trusted, calibration is * needed or the environment doesn't allow readings */ @@ -421,9 +428,10 @@ public abstract class SensorManager { * {@link SensorManager#getSensorList(int) getSensorList}. * * @param type - * of sensors requested + * of sensors requested * - * @return the default sensors matching the asked type. + * @return the default sensor matching the requested type if one exists and the application + * has the necessary permissions, or null otherwise. * * @see #getSensorList(int) * @see Sensor diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 8684a04..b66ec86 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -395,25 +395,12 @@ public class SystemSensorManager extends SensorManager { t.timestamp = timestamp; t.accuracy = inAccuracy; t.sensor = sensor; - switch (t.sensor.getType()) { - // Only report accuracy for sensors that support it. - case Sensor.TYPE_MAGNETIC_FIELD: - case Sensor.TYPE_ORIENTATION: - // call onAccuracyChanged() only if the value changes - final int accuracy = mSensorAccuracies.get(handle); - if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { - mSensorAccuracies.put(handle, t.accuracy); - mListener.onAccuracyChanged(t.sensor, t.accuracy); - } - break; - default: - // For other sensors, just report the accuracy once - if (mFirstEvent.get(handle) == false) { - mFirstEvent.put(handle, true); - mListener.onAccuracyChanged( - t.sensor, SENSOR_STATUS_ACCURACY_HIGH); - } - break; + + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + mListener.onAccuracyChanged(t.sensor, t.accuracy); } mListener.onSensorChanged(t); } diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java index 1af575f..ca71e81 100644 --- a/core/java/android/hardware/camera2/CameraAccessException.java +++ b/core/java/android/hardware/camera2/CameraAccessException.java @@ -114,7 +114,10 @@ public class CameraAccessException extends AndroidException { mReason = problem; } - private static String getDefaultMessage(int problem) { + /** + * @hide + */ + public static String getDefaultMessage(int problem) { switch (problem) { case CAMERA_IN_USE: return "The camera device is in use already"; diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 83db056..4a89fe7 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -84,9 +84,8 @@ public final class CameraManager { try { CameraBinderDecorator.throwOnError( CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor()); - } catch(CameraRuntimeException e) { - throw new IllegalStateException("Failed to setup camera vendor tags", - e.asChecked()); + } catch (CameraRuntimeException e) { + handleRecoverableSetupErrors(e, "Failed to set up vendor tags"); } try { @@ -426,6 +425,18 @@ public final class CameraManager { return mDeviceIdList; } + private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { + int problem = e.getReason(); + switch (problem) { + case CameraAccessException.CAMERA_DISCONNECTED: + String errorMsg = CameraAccessException.getDefaultMessage(problem); + Log.w(TAG, msg + ": " + errorMsg); + break; + default: + throw new IllegalStateException(msg, e.asChecked()); + } + } + // TODO: this class needs unit tests // TODO: extract class into top level private class CameraServiceListener extends ICameraServiceListener.Stub { diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java index cd86cd8..dcb3624 100644 --- a/core/java/android/hardware/hdmi/HdmiCecClient.java +++ b/core/java/android/hardware/hdmi/HdmiCecClient.java @@ -69,44 +69,28 @@ public final class HdmiCecClient { * Send <Active Source> message. */ public void sendActiveSource() { - try { - mService.sendActiveSource(mBinder); - } catch (RemoteException e) { - Log.e(TAG, "sendActiveSource threw exception ", e); - } + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); } /** * Send <Inactive Source> message. */ public void sendInactiveSource() { - try { - mService.sendInactiveSource(mBinder); - } catch (RemoteException e) { - Log.e(TAG, "sendInactiveSource threw exception ", e); - } + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); } /** * Send <Text View On> message. */ public void sendTextViewOn() { - try { - mService.sendTextViewOn(mBinder); - } catch (RemoteException e) { - Log.e(TAG, "sendTextViewOn threw exception ", e); - } + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); } /** * Send <Image View On> message. */ public void sendImageViewOn() { - try { - mService.sendImageViewOn(mBinder); - } catch (RemoteException e) { - Log.e(TAG, "sendImageViewOn threw exception ", e); - } + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); } /** @@ -116,11 +100,7 @@ public final class HdmiCecClient { * {@link HdmiCec#ADDR_TV}. */ public void sendGiveDevicePowerStatus(int address) { - try { - mService.sendGiveDevicePowerStatus(mBinder, address); - } catch (RemoteException e) { - Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e); - } + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); } /** @@ -133,11 +113,7 @@ public final class HdmiCecClient { * @return true if TV is on; otherwise false. */ public boolean isTvOn() { - try { - return mService.isTvOn(mBinder); - } catch (RemoteException e) { - Log.e(TAG, "isTvOn threw exception ", e); - } - return false; + Log.w(TAG, "In transition to HdmiControlManager. Will not work."); + return true; } } diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java index 10b058c..03c46d8 100644 --- a/core/java/android/hardware/hdmi/HdmiCecManager.java +++ b/core/java/android/hardware/hdmi/HdmiCecManager.java @@ -45,15 +45,7 @@ public final class HdmiCecManager { * @return {@link HdmiCecClient} instance. {@code null} on failure. */ public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) { - if (mService == null) { - return null; - } - try { - IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener)); - return HdmiCecClient.create(mService, b); - } catch (RemoteException e) { - return null; - } + return HdmiCecClient.create(mService, null); } private IHdmiCecListener getListenerWrapper(final HdmiCecClient.Listener listener) { diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java index ddaf870..62fa279 100644 --- a/core/java/android/hardware/hdmi/HdmiCecMessage.java +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java @@ -46,7 +46,7 @@ public final class HdmiCecMessage implements Parcelable { public HdmiCecMessage(int source, int destination, int opcode, byte[] params) { mSource = source; mDestination = destination; - mOpcode = opcode; + mOpcode = opcode & 0xFF; mParams = Arrays.copyOf(params, params.length); } @@ -123,6 +123,7 @@ public final class HdmiCecMessage implements Parcelable { * @param p HdmiCecMessage object to read the Rating from * @return a new HdmiCecMessage created from the data in the parcel */ + @Override public HdmiCecMessage createFromParcel(Parcel p) { int source = p.readInt(); int destination = p.readInt(); @@ -131,6 +132,7 @@ public final class HdmiCecMessage implements Parcelable { p.readByteArray(params); return new HdmiCecMessage(source, destination, opcode, params); } + @Override public HdmiCecMessage[] newArray(int size) { return new HdmiCecMessage[size]; } @@ -139,11 +141,40 @@ public final class HdmiCecMessage implements Parcelable { @Override public String toString() { StringBuffer s = new StringBuffer(); - s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode)); - for (byte data : mParams) { - s.append(String.format("%02X ", data)); + s.append(String.format("<%s> src: %d, dst: %d", + opcodeToString(mOpcode), mSource, mDestination)); + if (mParams.length > 0) { + s.append(", params:"); + for (byte data : mParams) { + s.append(String.format(" %02X", data)); + } } return s.toString(); } + + private static String opcodeToString(int opcode) { + switch (opcode) { + case HdmiCec.MESSAGE_FEATURE_ABORT: + return "Feature Abort"; + case HdmiCec.MESSAGE_CEC_VERSION: + return "CEC Version"; + case HdmiCec.MESSAGE_REQUEST_ARC_INITIATION: + return "Request ARC Initiation"; + case HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION: + return "Request ARC Termination"; + case HdmiCec.MESSAGE_REPORT_ARC_INITIATED: + return "Report ARC Initiated"; + case HdmiCec.MESSAGE_REPORT_ARC_TERMINATED: + return "Report ARC Terminated"; + case HdmiCec.MESSAGE_TEXT_VIEW_ON: + return "Text View On"; + case HdmiCec.MESSAGE_ACTIVE_SOURCE: + return "Active Source"; + case HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS: + return "Give Device Power Status"; + default: + return String.format("Opcode: %02X", opcode); + } + } } diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java index 83da29a..f0bd237 100644 --- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java +++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java @@ -90,7 +90,7 @@ public final class HdmiPlaybackClient { public void queryDisplayStatus(DisplayStatusCallback callback) { // TODO: PendingResult. try { - mService.oneTouchPlay(getCallbackWrapper(callback)); + mService.queryDisplayStatus(getCallbackWrapper(callback)); } catch (RemoteException e) { Log.e(TAG, "queryDisplayStatus threw exception ", e); } diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java new file mode 100644 index 0000000..7f8bc9f --- /dev/null +++ b/core/java/android/os/FileBridge.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 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.os; + +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.SOCK_STREAM; + +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import libcore.io.IoBridge; +import libcore.io.IoUtils; +import libcore.io.Memory; +import libcore.io.Streams; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.OutputStream; +import java.io.SyncFailedException; +import java.nio.ByteOrder; +import java.util.Arrays; + +/** + * Simple bridge that allows file access across process boundaries without + * returning the underlying {@link FileDescriptor}. This is useful when the + * server side needs to strongly assert that a client side is completely + * hands-off. + * + * @hide + */ +public class FileBridge extends Thread { + private static final String TAG = "FileBridge"; + + // TODO: consider extending to support bidirectional IO + + private static final int MSG_LENGTH = 8; + + /** CMD_WRITE [len] [data] */ + private static final int CMD_WRITE = 1; + /** CMD_FSYNC */ + private static final int CMD_FSYNC = 2; + + private FileDescriptor mTarget; + + private final FileDescriptor mServer = new FileDescriptor(); + private final FileDescriptor mClient = new FileDescriptor(); + + private volatile boolean mClosed; + + public FileBridge() { + try { + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient); + } catch (ErrnoException e) { + throw new RuntimeException("Failed to create bridge"); + } + } + + public boolean isClosed() { + return mClosed; + } + + public void setTargetFile(FileDescriptor target) { + mTarget = target; + } + + public FileDescriptor getClientSocket() { + return mClient; + } + + @Override + public void run() { + final byte[] temp = new byte[8192]; + try { + while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { + final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); + + if (cmd == CMD_WRITE) { + // Shuttle data into local file + int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); + while (len > 0) { + int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len)); + IoBridge.write(mTarget, temp, 0, n); + len -= n; + } + + } else if (cmd == CMD_FSYNC) { + // Sync and echo back to confirm + Os.fsync(mTarget); + IoBridge.write(mServer, temp, 0, MSG_LENGTH); + } + } + + // Client was closed; one last fsync + Os.fsync(mTarget); + + } catch (ErrnoException e) { + Log.e(TAG, "Failed during bridge: ", e); + } catch (IOException e) { + Log.e(TAG, "Failed during bridge: ", e); + } finally { + IoUtils.closeQuietly(mTarget); + IoUtils.closeQuietly(mServer); + IoUtils.closeQuietly(mClient); + mClosed = true; + } + } + + public static class FileBridgeOutputStream extends OutputStream { + private final FileDescriptor mClient; + private final byte[] mTemp = new byte[MSG_LENGTH]; + + public FileBridgeOutputStream(FileDescriptor client) { + mClient = client; + } + + @Override + public void close() throws IOException { + IoBridge.closeAndSignalBlockedThreads(mClient); + } + + @Override + public void flush() throws IOException { + Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN); + IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); + + // Wait for server to ack + if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) { + if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) { + return; + } + } + + throw new SyncFailedException("Failed to fsync() across bridge"); + } + + @Override + public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { + Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); + Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); + Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); + IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); + IoBridge.write(mClient, buffer, byteOffset, byteCount); + } + + @Override + public void write(int oneByte) throws IOException { + Streams.writeSingleByte(this, oneByte); + } + } +} diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 57ed979..474192f 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -70,6 +70,8 @@ public final class Trace { public static final long TRACE_TAG_DALVIK = 1L << 14; /** @hide */ public static final long TRACE_TAG_RS = 1L << 15; + /** @hide */ + public static final long TRACE_TAG_BIONIC = 1L << 16; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index cb0f142..c1d4d4c 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -74,7 +74,6 @@ public abstract class Vibrator { * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls. - * @hide */ public void vibrate(long milliseconds, int streamHint) { vibrate(Process.myUid(), mPackageName, milliseconds, streamHint); @@ -126,7 +125,6 @@ public abstract class Vibrator { * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls. - * @hide */ public void vibrate(long[] pattern, int repeat, int streamHint) { vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b53ea81..6db78f4 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1152,8 +1152,6 @@ public final class ContactsContract { * address book index, which is usually the first letter of the sort key. * When this parameter is supplied, the row counts are returned in the * cursor extras bundle. - * - * @hide */ public final static class ContactCounts { @@ -1163,7 +1161,24 @@ public final class ContactsContract { * first letter of the sort key. This parameter does not affect the main * content of the cursor. * - * @hide + * <p> + * <pre> + * Example: + * Uri uri = Contacts.CONTENT_URI.buildUpon() + * .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true") + * .build(); + * Cursor cursor = getContentResolver().query(uri, + * new String[] {Contacts.DISPLAY_NAME}, + * null, null, null); + * Bundle bundle = cursor.getExtras(); + * if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) && + * bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) { + * String sections[] = + * bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); + * int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); + * } + * </pre> + * </p> */ public static final String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras"; @@ -1171,8 +1186,6 @@ public final class ContactsContract { * The array of address book index titles, which are returned in the * same order as the data in the cursor. * <p>TYPE: String[]</p> - * - * @hide */ public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles"; @@ -1180,8 +1193,6 @@ public final class ContactsContract { * The array of group counts for the corresponding group. Contains the same number * of elements as the EXTRA_ADDRESS_BOOK_INDEX_TITLES array. * <p>TYPE: int[]</p> - * - * @hide */ public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts"; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e9ffc52..bec401e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4461,6 +4461,12 @@ public final class Settings { INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF; /** + * Whether the device should wake when the wake gesture sensor detects motion. + * @hide + */ + public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. diff --git a/core/java/android/speech/tts/Markup.java b/core/java/android/speech/tts/Markup.java new file mode 100644 index 0000000..c886e5d --- /dev/null +++ b/core/java/android/speech/tts/Markup.java @@ -0,0 +1,537 @@ +package android.speech.tts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class that provides markup to a synthesis request to control aspects of speech. + * <p> + * Markup itself is a feature agnostic data format; the {@link Utterance} class defines the currently + * available set of features and should be used to construct instances of the Markup class. + * </p> + * <p> + * A marked up sentence is a tree. Each node has a type, an optional plain text, a set of + * parameters, and a list of children. + * The <b>type</b> defines what it contains, e.g. "text", "date", "measure", etc. A Markup node + * can be either a part of sentence (often a leaf node), or node altering some property of its + * children (node with children). The top level node has to be of type "utterance" and its children + * are synthesized in order. + * The <b>plain text</b> is optional except for the top level node. If the synthesis engine does not + * support Markup at all, it should use the plain text of the top level node. If an engine does not + * recognize or support a node type, it will try to use the plain text of that node if provided. If + * the plain text is null, it will synthesize its children in order. + * <b>Parameters</b> are key-value pairs specific to each node type. In case of a date node the + * parameters may be for example "month: 7" and "day: 10". + * The <b>nested markups</b> are children and can for example be used to nest semiotic classes (a + * measure may have a node of type "decimal" as its child) or to modify some property of its + * children. See "plain text" on how they are processed if the parent of the children is unknown to + * the engine. + * <p> + */ +public final class Markup implements Parcelable { + + private String mType; + private String mPlainText; + + private Bundle mParameters = new Bundle(); + private List<Markup> mNestedMarkups = new ArrayList<Markup>(); + + private static final String TYPE = "type"; + private static final String PLAIN_TEXT = "plain_text"; + private static final String MARKUP = "markup"; + + private static final String IDENTIFIER_REGEX = "([0-9a-z_]+)"; + private static final Pattern legalIdentifierPattern = Pattern.compile(IDENTIFIER_REGEX); + + /** + * Constructs an empty markup. + */ + public Markup() {} + + /** + * Constructs a markup of the given type. + */ + public Markup(String type) { + setType(type); + } + + /** + * Returns the type of this node; can be null. + */ + public String getType() { + return mType; + } + + /** + * Sets the type of this node. can be null. May only contain [0-9a-z_]. + */ + public void setType(String type) { + if (type != null) { + Matcher matcher = legalIdentifierPattern.matcher(type); + if (!matcher.matches()) { + throw new IllegalArgumentException("Type cannot be empty and may only contain " + + "0-9, a-z and underscores."); + } + } + mType = type; + } + + /** + * Returns this node's plain text; can be null. + */ + public String getPlainText() { + return mPlainText; + } + + /** + * Sets this nodes's plain text; can be null. + */ + public void setPlainText(String plainText) { + mPlainText = plainText; + } + + /** + * Adds or modifies a parameter. + * @param key The key; may only contain [0-9a-z_] and cannot be "type" or "plain_text". + * @param value The value. + * @throws An {@link IllegalArgumentException} if the key is null or empty. + * @return this + */ + public Markup setParameter(String key, String value) { + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("Key cannot be null or empty."); + } + if (key.equals("type")) { + throw new IllegalArgumentException("Key cannot be \"type\"."); + } + if (key.equals("plain_text")) { + throw new IllegalArgumentException("Key cannot be \"plain_text\"."); + } + Matcher matcher = legalIdentifierPattern.matcher(key); + if (!matcher.matches()) { + throw new IllegalArgumentException("Key may only contain 0-9, a-z and underscores."); + } + + if (value != null) { + mParameters.putString(key, value); + } else { + removeParameter(key); + } + return this; + } + + /** + * Removes the parameter with the given key + */ + public void removeParameter(String key) { + mParameters.remove(key); + } + + /** + * Returns the value of the parameter. + * @param key The parameter key. + * @return The value of the parameter or null if the parameter is not set. + */ + public String getParameter(String key) { + return mParameters.getString(key); + } + + /** + * Returns the number of parameters that have been set. + */ + public int parametersSize() { + return mParameters.size(); + } + + /** + * Appends a child to the list of children + * @param markup The child. + * @return This instance. + * @throws {@link IllegalArgumentException} if markup is null. + */ + public Markup addNestedMarkup(Markup markup) { + if (markup == null) { + throw new IllegalArgumentException("Nested markup cannot be null"); + } + mNestedMarkups.add(markup); + return this; + } + + /** + * Removes the given node from its children. + * @param markup The child to remove. + * @return True if this instance was modified by this operation, false otherwise. + */ + public boolean removeNestedMarkup(Markup markup) { + return mNestedMarkups.remove(markup); + } + + /** + * Returns the index'th child. + * @param i The index of the child. + * @return The child. + * @throws {@link IndexOutOfBoundsException} if i < 0 or i >= nestedMarkupSize() + */ + public Markup getNestedMarkup(int i) { + return mNestedMarkups.get(i); + } + + + /** + * Returns the number of children. + */ + public int nestedMarkupSize() { + return mNestedMarkups.size(); + } + + /** + * Returns a string representation of this Markup instance. Can be deserialized back to a Markup + * instance with markupFromString(). + */ + public String toString() { + StringBuilder out = new StringBuilder(); + if (mType != null) { + out.append(TYPE + ": \"" + mType + "\""); + } + if (mPlainText != null) { + out.append(out.length() > 0 ? " " : ""); + out.append(PLAIN_TEXT + ": \"" + escapeQuotedString(mPlainText) + "\""); + } + // Sort the parameters alphabetically by key so we have a stable output. + SortedMap<String, String> sortedMap = new TreeMap<String, String>(); + for (String key : mParameters.keySet()) { + sortedMap.put(key, mParameters.getString(key)); + } + for (Map.Entry<String, String> entry : sortedMap.entrySet()) { + out.append(out.length() > 0 ? " " : ""); + out.append(entry.getKey() + ": \"" + escapeQuotedString(entry.getValue()) + "\""); + } + for (Markup m : mNestedMarkups) { + out.append(out.length() > 0 ? " " : ""); + String nestedStr = m.toString(); + if (nestedStr.isEmpty()) { + out.append(MARKUP + " {}"); + } else { + out.append(MARKUP + " { " + m.toString() + " }"); + } + } + return out.toString(); + } + + /** + * Escapes backslashes and double quotes in the plain text and parameter values before this + * instance is written to a string. + * @param str The string to escape. + * @return The escaped string. + */ + private static String escapeQuotedString(String str) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '"') { + out.append("\\\""); + } else if (str.charAt(i) == '\\') { + out.append("\\\\"); + } else { + out.append(c); + } + } + return out.toString(); + } + + /** + * The reverse of the escape method, returning plain text and parameter values to their original + * form. + * @param str An escaped string. + * @return The unescaped string. + */ + private static String unescapeQuotedString(String str) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + i++; + if (i >= str.length()) { + throw new IllegalArgumentException("Unterminated escape sequence in string: " + + str); + } + c = str.charAt(i); + if (c == '\\') { + out.append("\\"); + } else if (c == '"') { + out.append("\""); + } else { + throw new IllegalArgumentException("Unsupported escape sequence: \\" + c + + " in string " + str); + } + } else { + out.append(c); + } + } + return out.toString(); + } + + /** + * Returns true if the given string consists only of whitespace. + * @param str The string to check. + * @return True if the given string consists only of whitespace. + */ + private static boolean isWhitespace(String str) { + return Pattern.matches("\\s*", str); + } + + /** + * Parses the given string, and overrides the values of this instance with those contained + * in the given string. + * @param str The string to parse; can have superfluous whitespace. + * @return An empty string on success, else the remainder of the string that could not be + * parsed. + */ + private String fromReadableString(String str) { + while (!isWhitespace(str)) { + String newStr = matchValue(str); + if (newStr == null) { + newStr = matchMarkup(str); + + if (newStr == null) { + return str; + } + } + str = newStr; + } + return ""; + } + + // Matches: key : "value" + // where key is an identifier and value can contain escaped quotes + // there may be superflouous whitespace + // The value string may contain quotes and backslashes. + private static final String OPTIONAL_WHITESPACE = "\\s*"; + private static final String VALUE_REGEX = "((\\\\.|[^\\\"])*)"; + private static final String KEY_VALUE_REGEX = + "\\A" + OPTIONAL_WHITESPACE + // start of string + IDENTIFIER_REGEX + OPTIONAL_WHITESPACE + ":" + OPTIONAL_WHITESPACE + // key: + "\"" + VALUE_REGEX + "\""; // "value" + private static final Pattern KEY_VALUE_PATTERN = Pattern.compile(KEY_VALUE_REGEX); + + /** + * Tries to match a key-value pair at the start of the string. If found, add that as a parameter + * of this instance. + * @param str The string to parse. + * @return The remainder of the string without the parsed key-value pair on success, else null. + */ + private String matchValue(String str) { + // Matches: key: "value" + Matcher matcher = KEY_VALUE_PATTERN.matcher(str); + if (!matcher.find()) { + return null; + } + String key = matcher.group(1); + String value = matcher.group(2); + + if (key == null || value == null) { + return null; + } + String unescapedValue = unescapeQuotedString(value); + if (key.equals(TYPE)) { + this.mType = unescapedValue; + } else if (key.equals(PLAIN_TEXT)) { + this.mPlainText = unescapedValue; + } else { + setParameter(key, unescapedValue); + } + + return str.substring(matcher.group(0).length()); + } + + // matches 'markup {' + private static final Pattern OPEN_MARKUP_PATTERN = + Pattern.compile("\\A" + OPTIONAL_WHITESPACE + MARKUP + OPTIONAL_WHITESPACE + "\\{"); + // matches '}' + private static final Pattern CLOSE_MARKUP_PATTERN = + Pattern.compile("\\A" + OPTIONAL_WHITESPACE + "\\}"); + + /** + * Tries to parse a Markup specification from the start of the string. If so, add that markup to + * the list of nested Markup's of this instance. + * @param str The string to parse. + * @return The remainder of the string without the parsed Markup on success, else null. + */ + private String matchMarkup(String str) { + // find and strip "markup {" + Matcher matcher = OPEN_MARKUP_PATTERN.matcher(str); + + if (!matcher.find()) { + return null; + } + String strRemainder = str.substring(matcher.group(0).length()); + // parse and strip markup contents + Markup nestedMarkup = new Markup(); + strRemainder = nestedMarkup.fromReadableString(strRemainder); + + // find and strip "}" + Matcher matcherClose = CLOSE_MARKUP_PATTERN.matcher(strRemainder); + if (!matcherClose.find()) { + return null; + } + strRemainder = strRemainder.substring(matcherClose.group(0).length()); + + // Everything parsed, add markup + this.addNestedMarkup(nestedMarkup); + + // Return remainder + return strRemainder; + } + + /** + * Returns a Markup instance from the string representation generated by toString(). + * @param string The string representation generated by toString(). + * @return The new Markup instance. + * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed. + */ + public static Markup markupFromString(String string) throws IllegalArgumentException { + Markup m = new Markup(); + if (m.fromReadableString(string).isEmpty()) { + return m; + } else { + throw new IllegalArgumentException("Cannot parse input to Markup"); + } + } + + /** + * Compares the specified object with this Markup for equality. + * @return True if the given object is a Markup instance with the same type, plain text, + * parameters and the nested markups are also equal to each other and in the same order. + */ + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !(o instanceof Markup) ) return false; + Markup m = (Markup) o; + + if (nestedMarkupSize() != this.nestedMarkupSize()) { + return false; + } + + if (!(mType == null ? m.mType == null : mType.equals(m.mType))) { + return false; + } + if (!(mPlainText == null ? m.mPlainText == null : mPlainText.equals(m.mPlainText))) { + return false; + } + if (!equalBundles(mParameters, m.mParameters)) { + return false; + } + + for (int i = 0; i < this.nestedMarkupSize(); i++) { + if (!mNestedMarkups.get(i).equals(m.mNestedMarkups.get(i))) { + return false; + } + } + + return true; + } + + /** + * Checks if two bundles are equal to each other. Used by equals(o). + */ + private boolean equalBundles(Bundle one, Bundle two) { + if (one == null || two == null) { + return false; + } + + if(one.size() != two.size()) { + return false; + } + + Set<String> valuesOne = one.keySet(); + for(String key : valuesOne) { + Object valueOne = one.get(key); + Object valueTwo = two.get(key); + if (valueOne instanceof Bundle && valueTwo instanceof Bundle && + !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) { + return false; + } else if (valueOne == null) { + if (valueTwo != null || !two.containsKey(key)) { + return false; + } + } else if(!valueOne.equals(valueTwo)) { + return false; + } + } + return true; + } + + /** + * Returns an unmodifiable list of the children. + * @return An unmodifiable list of children that throws an {@link UnsupportedOperationException} + * if an attempt is made to modify it + */ + public List<Markup> getNestedMarkups() { + return Collections.unmodifiableList(mNestedMarkups); + } + + /** + * @hide + */ + public Markup(Parcel in) { + mType = in.readString(); + mPlainText = in.readString(); + mParameters = in.readBundle(); + in.readList(mNestedMarkups, Markup.class.getClassLoader()); + } + + /** + * Creates a deep copy of the given markup. + */ + public Markup(Markup markup) { + mType = markup.mType; + mPlainText = markup.mPlainText; + mParameters = markup.mParameters; + for (Markup nested : markup.getNestedMarkups()) { + addNestedMarkup(new Markup(nested)); + } + } + + /** + * @hide + */ + public int describeContents() { + return 0; + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mType); + dest.writeString(mPlainText); + dest.writeBundle(mParameters); + dest.writeList(mNestedMarkups); + } + + /** + * @hide + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Markup createFromParcel(Parcel in) { + return new Markup(in); + } + + public Markup[] newArray(int size) { + return new Markup[size]; + } + }; +} + diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java index a1da49c..130e3f9 100644 --- a/core/java/android/speech/tts/SynthesisRequestV2.java +++ b/core/java/android/speech/tts/SynthesisRequestV2.java @@ -4,11 +4,12 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.speech.tts.TextToSpeechClient.UtteranceId; +import android.util.Log; /** * Service-side representation of a synthesis request from a V2 API client. Contains: * <ul> - * <li>The utterance to synthesize</li> + * <li>The markup object to synthesize containing the utterance.</li> * <li>The id of the utterance (String, result of {@link UtteranceId#toUniqueString()}</li> * <li>The synthesis voice name (String, result of {@link VoiceInfo#getName()})</li> * <li>Voice parameters (Bundle of parameters)</li> @@ -16,8 +17,11 @@ import android.speech.tts.TextToSpeechClient.UtteranceId; * </ul> */ public final class SynthesisRequestV2 implements Parcelable { - /** Synthesis utterance. */ - private final String mText; + + private static final String TAG = "SynthesisRequestV2"; + + /** Synthesis markup */ + private final Markup mMarkup; /** Synthesis id. */ private final String mUtteranceId; @@ -34,9 +38,9 @@ public final class SynthesisRequestV2 implements Parcelable { /** * Constructor for test purposes. */ - public SynthesisRequestV2(String text, String utteranceId, String voiceName, + public SynthesisRequestV2(Markup markup, String utteranceId, String voiceName, Bundle voiceParams, Bundle audioParams) { - this.mText = text; + this.mMarkup = markup; this.mUtteranceId = utteranceId; this.mVoiceName = voiceName; this.mVoiceParams = voiceParams; @@ -49,15 +53,18 @@ public final class SynthesisRequestV2 implements Parcelable { * @hide */ public SynthesisRequestV2(Parcel in) { - this.mText = in.readString(); + this.mMarkup = (Markup) in.readValue(Markup.class.getClassLoader()); this.mUtteranceId = in.readString(); this.mVoiceName = in.readString(); this.mVoiceParams = in.readBundle(); this.mAudioParams = in.readBundle(); } - SynthesisRequestV2(String text, String utteranceId, RequestConfig rconfig) { - this.mText = text; + /** + * Constructor to request the synthesis of a sentence. + */ + SynthesisRequestV2(Markup markup, String utteranceId, RequestConfig rconfig) { + this.mMarkup = markup; this.mUtteranceId = utteranceId; this.mVoiceName = rconfig.getVoice().getName(); this.mVoiceParams = rconfig.getVoiceParams(); @@ -71,7 +78,7 @@ public final class SynthesisRequestV2 implements Parcelable { */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mText); + dest.writeValue(mMarkup); dest.writeString(mUtteranceId); dest.writeString(mVoiceName); dest.writeBundle(mVoiceParams); @@ -82,7 +89,18 @@ public final class SynthesisRequestV2 implements Parcelable { * @return the text which should be synthesized. */ public String getText() { - return mText; + if (mMarkup.getPlainText() == null) { + Log.e(TAG, "Plaintext of markup is null."); + return ""; + } + return mMarkup.getPlainText(); + } + + /** + * @return the markup which should be synthesized. + */ + public Markup getMarkup() { + return mMarkup; } /** diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java index 85f702b..e17b498 100644 --- a/core/java/android/speech/tts/TextToSpeechClient.java +++ b/core/java/android/speech/tts/TextToSpeechClient.java @@ -512,7 +512,6 @@ public class TextToSpeechClient { } } - /** * Connects the client to TTS service. This method returns immediately, and connects to the * service in the background. @@ -876,7 +875,7 @@ public class TextToSpeechClient { private static final String ACTION_QUEUE_SPEAK_NAME = "queueSpeak"; /** - * Speaks the string using the specified queuing strategy using current + * Speaks the string using the specified queuing strategy and the current * voice. This method is asynchronous, i.e. the method just adds the request * to the queue of TTS requests and then returns. The synthesis might not * have finished (or even started!) at the time when this method returns. @@ -887,12 +886,35 @@ public class TextToSpeechClient { * in {@link RequestCallbacks}. * @param config Synthesis request configuration. Can't be null. Has to contain a * voice. - * @param callbacks Synthesis request callbacks. If null, default request + * @param callbacks Synthesis request callbacks. If null, the default request * callbacks object will be used. */ public void queueSpeak(final String utterance, final UtteranceId utteranceId, final RequestConfig config, final RequestCallbacks callbacks) { + queueSpeak(createMarkupFromString(utterance), utteranceId, config, callbacks); + } + + /** + * Speaks the {@link Markup} (which can be constructed with {@link Utterance}) using + * the specified queuing strategy and the current voice. This method is + * asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even + * started!) at the time when this method returns. + * + * @param markup The Markup to be spoken. The written equivalent of the spoken + * text should be no longer than 1000 characters. + * @param utteranceId Unique identificator used to track the synthesis progress + * in {@link RequestCallbacks}. + * @param config Synthesis request configuration. Can't be null. Has to contain a + * voice. + * @param callbacks Synthesis request callbacks. If null, the default request + * callbacks object will be used. + */ + public void queueSpeak(final Markup markup, + final UtteranceId utteranceId, + final RequestConfig config, + final RequestCallbacks callbacks) { runAction(new Action(ACTION_QUEUE_SPEAK_NAME) { @Override public void run(ITextToSpeechService service) throws RemoteException { @@ -908,7 +930,7 @@ public class TextToSpeechClient { int queueResult = service.speakV2( getCallerIdentity(), - new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), config)); + new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config)); if (queueResult != Status.SUCCESS) { removeCallbackAndErr(utteranceId.toUniqueString(), queueResult); } @@ -931,12 +953,37 @@ public class TextToSpeechClient { * @param outputFile File to write the generated audio data to. * @param config Synthesis request configuration. Can't be null. Have to contain a * voice. - * @param callbacks Synthesis request callbacks. If null, default request + * @param callbacks Synthesis request callbacks. If null, the default request * callbacks object will be used. */ public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId, final File outputFile, final RequestConfig config, final RequestCallbacks callbacks) { + queueSynthesizeToFile(createMarkupFromString(utterance), utteranceId, outputFile, config, callbacks); + } + + /** + * Synthesizes the given {@link Markup} (can be constructed with {@link Utterance}) + * to a file using the specified parameters. This method is asynchronous, i.e. the + * method just adds the request to the queue of TTS requests and then returns. The + * synthesis might not have finished (or even started!) at the time when this method + * returns. + * + * @param markup The Markup that should be synthesized. The written equivalent of + * the spoken text should be no longer than 1000 characters. + * @param utteranceId Unique identificator used to track the synthesis progress + * in {@link RequestCallbacks}. + * @param outputFile File to write the generated audio data to. + * @param config Synthesis request configuration. Can't be null. Have to contain a + * voice. + * @param callbacks Synthesis request callbacks. If null, the default request + * callbacks object will be used. + */ + public void queueSynthesizeToFile( + final Markup markup, + final UtteranceId utteranceId, + final File outputFile, final RequestConfig config, + final RequestCallbacks callbacks) { runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) { @Override public void run(ITextToSpeechService service) throws RemoteException { @@ -964,8 +1011,7 @@ public class TextToSpeechClient { int queueResult = service.synthesizeToFileDescriptorV2(getCallerIdentity(), fileDescriptor, - new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), - config)); + new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config)); fileDescriptor.close(); if (queueResult != Status.SUCCESS) { removeCallbackAndErr(utteranceId.toUniqueString(), queueResult); @@ -981,6 +1027,13 @@ public class TextToSpeechClient { }); } + private static Markup createMarkupFromString(String str) { + return new Utterance() + .append(new Utterance.TtsText(str)) + .setNoWarningOnFallback(true) + .createMarkup(); + } + private static final String ACTION_QUEUE_SILENCE_NAME = "queueSilence"; /** diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 6b899d9..14a4024 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -352,6 +352,12 @@ public abstract class TextToSpeechService extends Service { params.putString(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS, "true"); } + String noWarning = request.getMarkup().getParameter(Utterance.KEY_NO_WARNING_ON_FALLBACK); + if (noWarning == null || noWarning.equals("false")) { + Log.w("TextToSpeechService", "The synthesis engine does not support Markup, falling " + + "back to the given plain text."); + } + // Build V1 request SynthesisRequest requestV1 = new SynthesisRequest(request.getText(), params); Locale locale = selectedVoice.getLocale(); @@ -856,14 +862,53 @@ public abstract class TextToSpeechService extends Service { } } + /** + * Estimate of the character count equivalent of a Markup instance. Calculated + * by summing the characters of all Markups of type "text". Each other node + * is counted as a single character, as the character count of other nodes + * is non-trivial to calculate and we don't want to accept arbitrarily large + * requests. + */ + private int estimateSynthesisLengthFromMarkup(Markup m) { + int size = 0; + if (m.getType() != null && + m.getType().equals("text") && + m.getParameter("text") != null) { + size += m.getParameter("text").length(); + } else if (m.getType() == null || + !m.getType().equals("utterance")) { + size += 1; + } + for (Markup nested : m.getNestedMarkups()) { + size += estimateSynthesisLengthFromMarkup(nested); + } + return size; + } + @Override public boolean isValid() { - if (mSynthesisRequest.getText() == null) { - Log.e(TAG, "null synthesis text"); + if (mSynthesisRequest.getMarkup() == null) { + Log.e(TAG, "No markup in request."); return false; } - if (mSynthesisRequest.getText().length() >= TextToSpeech.getMaxSpeechInputLength()) { - Log.w(TAG, "Text too long: " + mSynthesisRequest.getText().length() + " chars"); + String type = mSynthesisRequest.getMarkup().getType(); + if (type == null) { + Log.w(TAG, "Top level markup node should have type \"utterance\", not null"); + return false; + } else if (!type.equals("utterance")) { + Log.w(TAG, "Top level markup node should have type \"utterance\" instead of " + + "\"" + type + "\""); + return false; + } + + int estimate = estimateSynthesisLengthFromMarkup(mSynthesisRequest.getMarkup()); + if (estimate >= TextToSpeech.getMaxSpeechInputLength()) { + Log.w(TAG, "Text too long: estimated size of text was " + estimate + " chars."); + return false; + } + + if (estimate <= 0) { + Log.e(TAG, "null synthesis text"); return false; } diff --git a/core/java/android/speech/tts/Utterance.java b/core/java/android/speech/tts/Utterance.java new file mode 100644 index 0000000..0a29283 --- /dev/null +++ b/core/java/android/speech/tts/Utterance.java @@ -0,0 +1,595 @@ +package android.speech.tts; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class acts as a builder for {@link Markup} instances. + * <p> + * Each Utterance consists of a list of the semiotic classes ({@link Utterance.TtsCardinal} and + * {@link Utterance.TtsText}). + * <p>Each semiotic class can be supplied with morphosyntactic features + * (gender, animacy, multiplicity and case), it is up to the synthesis engine to use this + * information during synthesis. + * Examples where morphosyntactic features matter: + * <ul> + * <li>In French, the number one is verbalized differently based on the gender of the noun + * it modifies. "un homme" (one man) versus "une femme" (one woman). + * <li>In German the grammatical case (accusative, locative, etc) needs to be included to be + * verbalize correctly. In German you'd have the sentence "Sie haben 1 kilometer vor Ihnen" (You + * have 1 kilometer ahead of you), "1" in this case needs to become inflected to the accusative + * form ("einen") instead of the nominative form "ein". + * </p> + * <p> + * Utterance usage example: + * Markup m1 = new Utterance().append("The Eiffel Tower is") + * .append(new TtsCardinal(324)) + * .append("meters tall."); + * Markup m2 = new Utterance().append("Sie haben") + * .append(new TtsCardinal(1).setGender(Utterance.GENDER_MALE) + * .append("Tag frei."); + * </p> + */ +public class Utterance { + + /*** + * Toplevel type of markup representation. + */ + public static final String TYPE_UTTERANCE = "utterance"; + /*** + * The no_warning_on_fallback parameter can be set to "false" or "true", true indicating that + * no warning will be given when the synthesizer does not support Markup. This is used when + * the user only provides a string to the API instead of a markup. + */ + public static final String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback"; + + // Gender. + public final static int GENDER_UNKNOWN = 0; + public final static int GENDER_NEUTRAL = 1; + public final static int GENDER_MALE = 2; + public final static int GENDER_FEMALE = 3; + + // Animacy. + public final static int ANIMACY_UNKNOWN = 0; + public final static int ANIMACY_ANIMATE = 1; + public final static int ANIMACY_INANIMATE = 2; + + // Multiplicity. + public final static int MULTIPLICITY_UNKNOWN = 0; + public final static int MULTIPLICITY_SINGLE = 1; + public final static int MULTIPLICITY_DUAL = 2; + public final static int MULTIPLICITY_PLURAL = 3; + + // Case. + public final static int CASE_UNKNOWN = 0; + public final static int CASE_NOMINATIVE = 1; + public final static int CASE_ACCUSATIVE = 2; + public final static int CASE_DATIVE = 3; + public final static int CASE_ABLATIVE = 4; + public final static int CASE_GENITIVE = 5; + public final static int CASE_VOCATIVE = 6; + public final static int CASE_LOCATIVE = 7; + public final static int CASE_INSTRUMENTAL = 8; + + private List<AbstractTts<? extends AbstractTts<?>>> says = + new ArrayList<AbstractTts<? extends AbstractTts<?>>>(); + Boolean mNoWarningOnFallback = null; + + /** + * Objects deriving from this class can be appended to a Utterance. This class uses generics + * so method from this class can return instances of its child classes, resulting in a better + * API (CRTP pattern). + */ + public static abstract class AbstractTts<C extends AbstractTts<C>> { + + protected Markup mMarkup = new Markup(); + + /** + * Empty constructor. + */ + protected AbstractTts() { + } + + /** + * Construct with Markup. + * @param markup + */ + protected AbstractTts(Markup markup) { + mMarkup = markup; + } + + /** + * Returns the type of this class, e.g. "cardinal" or "measure". + * @return The type. + */ + public String getType() { + return mMarkup.getType(); + } + + /** + * A fallback plain text can be provided, in case the engine does not support this class + * type, or even Markup altogether. + * @param plainText A string with the plain text. + * @return This instance. + */ + @SuppressWarnings("unchecked") + public C setPlainText(String plainText) { + mMarkup.setPlainText(plainText); + return (C) this; + } + + /** + * Returns the plain text (fallback) string. + * @return Plain text string or null if not set. + */ + public String getPlainText() { + return mMarkup.getPlainText(); + } + + /** + * Populates the plainText if not set and builds a Markup instance. + * @return The Markup object describing this instance. + */ + public Markup getMarkup() { + return new Markup(mMarkup); + } + + @SuppressWarnings("unchecked") + protected C setParameter(String key, String value) { + mMarkup.setParameter(key, value); + return (C) this; + } + + protected String getParameter(String key) { + return mMarkup.getParameter(key); + } + + @SuppressWarnings("unchecked") + protected C removeParameter(String key) { + mMarkup.removeParameter(key); + return (C) this; + } + + /** + * Returns a string representation of this instance, can be deserialized to an equal + * Utterance instance. + */ + public String toString() { + return mMarkup.toString(); + } + + /** + * Returns a generated plain text alternative for this instance if this instance isn't + * better representated by the list of it's children. + * @return Best effort plain text representation of this instance, can be null. + */ + public String generatePlainText() { + return null; + } + } + + public static abstract class AbstractTtsSemioticClass<C extends AbstractTtsSemioticClass<C>> + extends AbstractTts<C> { + // Keys. + private static final String KEY_GENDER = "gender"; + private static final String KEY_ANIMACY = "animacy"; + private static final String KEY_MULTIPLICITY = "multiplicity"; + private static final String KEY_CASE = "case"; + + protected AbstractTtsSemioticClass() { + super(); + } + + protected AbstractTtsSemioticClass(Markup markup) { + super(markup); + } + + @SuppressWarnings("unchecked") + public C setGender(int gender) { + if (gender < 0 || gender > 3) { + throw new IllegalArgumentException("Only four types of gender can be set: " + + "unknown, neutral, maculine and female."); + } + if (gender != GENDER_UNKNOWN) { + setParameter(KEY_GENDER, String.valueOf(gender)); + } else { + setParameter(KEY_GENDER, null); + } + return (C) this; + } + + public int getGender() { + String gender = mMarkup.getParameter(KEY_GENDER); + return gender != null ? Integer.valueOf(gender) : GENDER_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setAnimacy(int animacy) { + if (animacy < 0 || animacy > 2) { + throw new IllegalArgumentException( + "Only two types of animacy can be set: unknown, animate and inanimate"); + } + if (animacy != ANIMACY_UNKNOWN) { + setParameter(KEY_ANIMACY, String.valueOf(animacy)); + } else { + setParameter(KEY_ANIMACY, null); + } + return (C) this; + } + + public int getAnimacy() { + String animacy = getParameter(KEY_ANIMACY); + return animacy != null ? Integer.valueOf(animacy) : ANIMACY_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setMultiplicity(int multiplicity) { + if (multiplicity < 0 || multiplicity > 3) { + throw new IllegalArgumentException( + "Only four types of multiplicity can be set: unknown, single, dual and " + + "plural."); + } + if (multiplicity != MULTIPLICITY_UNKNOWN) { + setParameter(KEY_MULTIPLICITY, String.valueOf(multiplicity)); + } else { + setParameter(KEY_MULTIPLICITY, null); + } + return (C) this; + } + + public int getMultiplicity() { + String multiplicity = mMarkup.getParameter(KEY_MULTIPLICITY); + return multiplicity != null ? Integer.valueOf(multiplicity) : MULTIPLICITY_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setCase(int grammaticalCase) { + if (grammaticalCase < 0 || grammaticalCase > 8) { + throw new IllegalArgumentException( + "Only nine types of grammatical case can be set."); + } + if (grammaticalCase != CASE_UNKNOWN) { + setParameter(KEY_CASE, String.valueOf(grammaticalCase)); + } else { + setParameter(KEY_CASE, null); + } + return (C) this; + } + + public int getCase() { + String grammaticalCase = mMarkup.getParameter(KEY_CASE); + return grammaticalCase != null ? Integer.valueOf(grammaticalCase) : CASE_UNKNOWN; + } + } + + /** + * Class that contains regular text, synthesis engine pronounces it using its regular pipeline. + * Parameters: + * <ul> + * <li>Text: the text to synthesize</li> + * </ul> + */ + public static class TtsText extends AbstractTtsSemioticClass<TtsText> { + + // The type of this node. + protected static final String TYPE_TEXT = "text"; + // The text parameter stores the text to be synthesized. + private static final String KEY_TEXT = "text"; + + /** + * Default constructor. + */ + public TtsText() { + mMarkup.setType(TYPE_TEXT); + } + + /** + * Constructor that sets the text to be synthesized. + * @param text The text to be synthesized. + */ + public TtsText(String text) { + this(); + setText(text); + } + + /** + * Constructs a TtsText with the values of the Markup, does not check if the given Markup is + * of the right type. + */ + private TtsText(Markup markup) { + super(markup); + } + + /** + * Sets the text to be synthesized. + * @return This instance. + */ + public TtsText setText(String text) { + setParameter(KEY_TEXT, text); + return this; + } + + /** + * Returns the text to be synthesized. + * @return This instance. + */ + public String getText() { + return getParameter(KEY_TEXT); + } + + /** + * Generates a best effort plain text, in this case simply the text. + */ + @Override + public String generatePlainText() { + return getText(); + } + } + + /** + * Contains a cardinal. + * Parameters: + * <ul> + * <li>integer: the integer to synthesize</li> + * </ul> + */ + public static class TtsCardinal extends AbstractTtsSemioticClass<TtsCardinal> { + + // The type of this node. + protected static final String TYPE_CARDINAL = "cardinal"; + // The parameter integer stores the integer to synthesize. + private static final String KEY_INTEGER = "integer"; + + /** + * Default constructor. + */ + public TtsCardinal() { + mMarkup.setType(TYPE_CARDINAL); + } + + /** + * Constructor that sets the integer to be synthesized. + */ + public TtsCardinal(int integer) { + this(); + setInteger(integer); + } + + /** + * Constructor that sets the integer to be synthesized. + */ + public TtsCardinal(String integer) { + this(); + setInteger(integer); + } + + /** + * Constructs a TtsText with the values of the Markup. + * Does not check if the given Markup is of the right type. + */ + private TtsCardinal(Markup markup) { + super(markup); + } + + /** + * Sets the integer. + * @return This instance. + */ + public TtsCardinal setInteger(int integer) { + return setInteger(String.valueOf(integer)); + } + + /** + * Sets the integer. + * @param integer A non-empty string of digits with an optional '-' in front. + * @return This instance. + */ + public TtsCardinal setInteger(String integer) { + if (!integer.matches("-?\\d+")) { + throw new IllegalArgumentException("Expected a cardinal: \"" + integer + "\""); + } + setParameter(KEY_INTEGER, integer); + return this; + } + + /** + * Returns the integer parameter. + */ + public String getInteger() { + return getParameter(KEY_INTEGER); + } + + /** + * Generates a best effort plain text, in this case simply the integer. + */ + @Override + public String generatePlainText() { + return getInteger(); + } + } + + /** + * Default constructor. + */ + public Utterance() {} + + /** + * Returns the plain text of a given Markup if it was set; if it's not set, recursively call the + * this same method on its children. + */ + private String constructPlainText(Markup m) { + StringBuilder plainText = new StringBuilder(); + if (m.getPlainText() != null) { + plainText.append(m.getPlainText()); + } else { + for (Markup nestedMarkup : m.getNestedMarkups()) { + String nestedPlainText = constructPlainText(nestedMarkup); + if (!nestedPlainText.isEmpty()) { + if (plainText.length() != 0) { + plainText.append(" "); + } + plainText.append(nestedPlainText); + } + } + } + return plainText.toString(); + } + + /** + * Creates a Markup instance with auto generated plain texts for the relevant nodes, in case the + * user has not provided one already. + * @return A Markup instance representing this utterance. + */ + public Markup createMarkup() { + Markup markup = new Markup(TYPE_UTTERANCE); + StringBuilder plainText = new StringBuilder(); + for (AbstractTts<? extends AbstractTts<?>> say : says) { + // Get a copy of this markup, and generate a plaintext for it if is not set. + Markup sayMarkup = say.getMarkup(); + if (sayMarkup.getPlainText() == null) { + sayMarkup.setPlainText(say.generatePlainText()); + } + if (plainText.length() != 0) { + plainText.append(" "); + } + plainText.append(constructPlainText(sayMarkup)); + markup.addNestedMarkup(sayMarkup); + } + if (mNoWarningOnFallback != null) { + markup.setParameter(KEY_NO_WARNING_ON_FALLBACK, + mNoWarningOnFallback ? "true" : "false"); + } + markup.setPlainText(plainText.toString()); + return markup; + } + + /** + * Appends an element to this Utterance instance. + * @return this instance + */ + public Utterance append(AbstractTts<? extends AbstractTts<?>> say) { + says.add(say); + return this; + } + + private Utterance append(Markup markup) { + if (markup.getType().equals(TtsText.TYPE_TEXT)) { + append(new TtsText(markup)); + } else if (markup.getType().equals(TtsCardinal.TYPE_CARDINAL)) { + append(new TtsCardinal(markup)); + } else { + // Unknown node, a class we don't know about. + if (markup.getPlainText() != null) { + append(new TtsText(markup.getPlainText())); + } else { + // No plainText specified; add its children + // seperately. In case of a new prosody node, + // we would still verbalize it correctly. + for (Markup nested : markup.getNestedMarkups()) { + append(nested); + } + } + } + return this; + } + + /** + * Returns a string representation of this Utterance instance. Can be deserialized back to an + * Utterance instance with utteranceFromString(). Can be used to store utterances to be used + * at a later time. + */ + public String toString() { + String out = "type: \"" + TYPE_UTTERANCE + "\""; + if (mNoWarningOnFallback != null) { + out += " no_warning_on_fallback: \"" + (mNoWarningOnFallback ? "true" : "false") + "\""; + } + for (AbstractTts<? extends AbstractTts<?>> say : says) { + out += " markup { " + say.getMarkup().toString() + " }"; + } + return out; + } + + /** + * Returns an Utterance instance from the string representation generated by toString(). + * @param string The string representation generated by toString(). + * @return The new Utterance instance. + * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed. + */ + static public Utterance utteranceFromString(String string) throws IllegalArgumentException { + Utterance utterance = new Utterance(); + Markup markup = Markup.markupFromString(string); + if (!markup.getType().equals(TYPE_UTTERANCE)) { + throw new IllegalArgumentException("Top level markup should be of type \"" + + TYPE_UTTERANCE + "\", but was of type \"" + + markup.getType() + "\".") ; + } + for (Markup nestedMarkup : markup.getNestedMarkups()) { + utterance.append(nestedMarkup); + } + return utterance; + } + + /** + * Appends a new TtsText with the given text. + * @param text The text to synthesize. + * @return This instance. + */ + public Utterance append(String text) { + return append(new TtsText(text)); + } + + /** + * Appends a TtsCardinal representing the given number. + * @param integer The integer to synthesize. + * @return this + */ + public Utterance append(int integer) { + return append(new TtsCardinal(integer)); + } + + /** + * Returns the n'th element in this Utterance. + * @param i The index. + * @return The n'th element in this Utterance. + * @throws {@link IndexOutOfBoundsException} - if i < 0 || i >= size() + */ + public AbstractTts<? extends AbstractTts<?>> get(int i) { + return says.get(i); + } + + /** + * Returns the number of elements in this Utterance. + * @return The number of elements in this Utterance. + */ + public int size() { + return says.size(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !(o instanceof Utterance) ) return false; + Utterance utt = (Utterance) o; + + if (says.size() != utt.says.size()) { + return false; + } + + for (int i = 0; i < says.size(); i++) { + if (!says.get(i).getMarkup().equals(utt.says.get(i).getMarkup())) { + return false; + } + } + return true; + } + + /** + * Can be set to true or false, true indicating that the user provided only a string to the API, + * at which the system will not issue a warning if the synthesizer falls back onto the plain + * text when the synthesizer does not support Markup. + */ + public Utterance setNoWarningOnFallback(boolean noWarning) { + mNoWarningOnFallback = noWarning; + return this; + } +} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c15ce44..5cd3d62 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -38,11 +38,11 @@ public class SurfaceControl { private static native void nativeDestroy(long nativeObject); private static native Bitmap nativeScreenshot(IBinder displayToken, - int width, int height, int minLayer, int maxLayer, boolean allLayers, - boolean useIdentityTransform); + Rect sourceCrop, int width, int height, int minLayer, int maxLayer, + boolean allLayers, boolean useIdentityTransform); private static native void nativeScreenshot(IBinder displayToken, Surface consumer, - int width, int height, int minLayer, int maxLayer, boolean allLayers, - boolean useIdentityTransform); + Rect sourceCrop, int width, int height, int minLayer, int maxLayer, + boolean allLayers, boolean useIdentityTransform); private static native void nativeOpenTransaction(); private static native void nativeCloseTransaction(); @@ -597,8 +597,8 @@ public class SurfaceControl { public static void screenshot(IBinder display, Surface consumer, int width, int height, int minLayer, int maxLayer, boolean useIdentityTransform) { - screenshot(display, consumer, width, height, minLayer, maxLayer, false, - useIdentityTransform); + screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer, + false, useIdentityTransform); } /** @@ -613,7 +613,7 @@ public class SurfaceControl { */ public static void screenshot(IBinder display, Surface consumer, int width, int height) { - screenshot(display, consumer, width, height, 0, 0, true, false); + screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false); } /** @@ -623,7 +623,7 @@ public class SurfaceControl { * @param consumer The {@link Surface} to take the screenshot into. */ public static void screenshot(IBinder display, Surface consumer) { - screenshot(display, consumer, 0, 0, 0, 0, true, false); + screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false); } /** @@ -634,6 +634,8 @@ public class SurfaceControl { * the versions that use a {@link Surface} instead, such as * {@link SurfaceControl#screenshot(IBinder, Surface)}. * + * @param sourceCrop The portion of the screen to capture into the Bitmap; + * caller may pass in 'new Rect()' if no cropping is desired. * @param width The desired width of the returned bitmap; the raw * screen will be scaled down to this size. * @param height The desired height of the returned bitmap; the raw @@ -649,13 +651,13 @@ public class SurfaceControl { * if an error occurs. Make sure to call Bitmap.recycle() as soon as * possible, once its content is not needed anymore. */ - public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer, - boolean useIdentityTransform) { + public static Bitmap screenshot(Rect sourceCrop, int width, int height, + int minLayer, int maxLayer, boolean useIdentityTransform) { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false, - useIdentityTransform); + return nativeScreenshot(displayToken, sourceCrop, width, height, + minLayer, maxLayer, false, useIdentityTransform); } /** @@ -674,10 +676,10 @@ public class SurfaceControl { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, 0, 0, true, false); + return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, false); } - private static void screenshot(IBinder display, Surface consumer, + private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean allLayers, boolean useIdentityTransform) { if (display == null) { @@ -686,7 +688,7 @@ public class SurfaceControl { if (consumer == null) { throw new IllegalArgumentException("consumer must not be null"); } - nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers, - useIdentityTransform); + nativeScreenshot(display, consumer, sourceCrop, width, height, + minLayer, maxLayer, allLayers, useIdentityTransform); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 025cf69..ce266d7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9236,6 +9236,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Request unbuffered dispatch of the given stream of MotionEvents to this View. + * + * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input + * system not batch {@link MotionEvent}s but instead deliver them as soon as they're + * available. This method should only be called for touch events. + * + * <p class="note">This api is not intended for most applications. Buffered dispatch + * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent + * streams will not improve your input latency. Side effects include: increased latency, + * jittery scrolls and inability to take advantage of system resampling. Talk to your input + * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for + * you.</p> + */ + public final void requestUnbufferedDispatch(MotionEvent event) { + final int action = event.getAction(); + if (mAttachInfo == null + || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE + || !event.isTouchEvent()) { + return; + } + mAttachInfo.mUnbufferedDispatchRequested = true; + } + + /** * Set flags controlling behavior of this view. * * @param flags Constant indicating the value which should be set @@ -19760,6 +19784,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mInTouchMode; /** + * Indicates whether the view has requested unbuffered input dispatching for the current + * event stream. + */ + boolean mUnbufferedDispatchRequested; + + /** * Indicates that ViewAncestor should trigger a global layout change * the next time it performs a traversal */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index aa06d15..ac25b57 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -230,6 +230,7 @@ public final class ViewRootImpl implements ViewParent, QueuedInputEvent mPendingInputEventTail; int mPendingInputEventCount; boolean mProcessInputEventsScheduled; + boolean mUnbufferedInputDispatch; String mPendingInputEventQueueLengthCounterName = "pq"; InputStage mFirstInputStage; @@ -1016,7 +1017,9 @@ public final class ViewRootImpl implements ViewParent, mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); - scheduleConsumeBatchedInput(); + if (!mUnbufferedInputDispatch) { + scheduleConsumeBatchedInput(); + } notifyRendererOfFramePending(); } } @@ -2616,7 +2619,7 @@ public final class ViewRootImpl implements ViewParent, } final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); - final Rect bounds = mView.mAttachInfo.mTmpInvalRect; + final Rect bounds = mAttachInfo.mTmpInvalRect; if (provider == null) { host.getBoundsOnScreen(bounds); } else if (mAccessibilityFocusedVirtualView != null) { @@ -3898,6 +3901,18 @@ public final class ViewRootImpl implements ViewParent, } } + @Override + protected void onDeliverToNext(QueuedInputEvent q) { + if (mUnbufferedInputDispatch + && q.mEvent instanceof MotionEvent + && ((MotionEvent)q.mEvent).isTouchEvent() + && isTerminalInputEvent(q.mEvent)) { + mUnbufferedInputDispatch = false; + scheduleConsumeBatchedInput(); + } + super.onDeliverToNext(q); + } + private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; @@ -4010,10 +4025,15 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; - if (mView.dispatchPointerEvent(event)) { - return FINISH_HANDLED; + mAttachInfo.mUnbufferedDispatchRequested = false; + boolean handled = mView.dispatchPointerEvent(event); + if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { + mUnbufferedInputDispatch = true; + if (mConsumeBatchedInputScheduled) { + scheduleConsumeBatchedInputImmediately(); + } } - return FORWARD; + return handled ? FINISH_HANDLED : FORWARD; } private int processTrackballEvent(QueuedInputEvent q) { @@ -5278,6 +5298,8 @@ public final class ViewRootImpl implements ViewParent, writer.print(" mRemoved="); writer.println(mRemoved); writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled="); writer.println(mConsumeBatchedInputScheduled); + writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled="); + writer.println(mConsumeBatchedInputImmediatelyScheduled); writer.print(innerPrefix); writer.print("mPendingInputEventCount="); writer.println(mPendingInputEventCount); writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled="); @@ -5688,6 +5710,7 @@ public final class ViewRootImpl implements ViewParent, private void finishInputEvent(QueuedInputEvent q) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent", q.mEvent.getSequenceNumber()); + if (q.mReceiver != null) { boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; q.mReceiver.finishInputEvent(q.mEvent, handled); @@ -5727,15 +5750,25 @@ public final class ViewRootImpl implements ViewParent, } } + void scheduleConsumeBatchedInputImmediately() { + if (!mConsumeBatchedInputImmediatelyScheduled) { + unscheduleConsumeBatchedInput(); + mConsumeBatchedInputImmediatelyScheduled = true; + mHandler.post(mConsumeBatchedInputImmediatelyRunnable); + } + } + void doConsumeBatchedInput(long frameTimeNanos) { if (mConsumeBatchedInputScheduled) { mConsumeBatchedInputScheduled = false; if (mInputEventReceiver != null) { - if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) { + if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos) + && frameTimeNanos != -1) { // If we consumed a batch here, we want to go ahead and schedule the // consumption of batched input events on the next frame. Otherwise, we would // wait until we have more input events pending and might get starved by other - // things occurring in the process. + // things occurring in the process. If the frame time is -1, however, then + // we're in a non-batching mode, so there's no need to schedule this. scheduleConsumeBatchedInput(); } } @@ -5763,7 +5796,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void onBatchedInputEventPending() { - scheduleConsumeBatchedInput(); + if (mUnbufferedInputDispatch) { + super.onBatchedInputEventPending(); + } else { + scheduleConsumeBatchedInput(); + } } @Override @@ -5784,6 +5821,16 @@ public final class ViewRootImpl implements ViewParent, new ConsumeBatchedInputRunnable(); boolean mConsumeBatchedInputScheduled; + final class ConsumeBatchedInputImmediatelyRunnable implements Runnable { + @Override + public void run() { + doConsumeBatchedInput(-1); + } + } + final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable = + new ConsumeBatchedInputImmediatelyRunnable(); + boolean mConsumeBatchedInputImmediatelyScheduled; + final class InvalidateOnAnimationRunnable implements Runnable { private boolean mPosted; private final ArrayList<View> mViews = new ArrayList<View>(); diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 628da3c..84f395a 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -427,8 +427,12 @@ public class SpellCheckerSession { @Override public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { - mHandler.sendMessage( - Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); + synchronized (this) { + if (mHandler != null) { + mHandler.sendMessage(Message.obtain(mHandler, + MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); + } + } } } diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 8511601..defc26c 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -104,14 +104,16 @@ import static java.lang.Math.min; * * <h4>Excess Space Distribution</h4> * - * GridLayout's distribution of excess space is based on <em>priority</em> - * rather than <em>weight</em>. + * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight. + * In the event that no weights are specified, the previous conventions are respected and + * columns and rows are taken as flexible if their views specify some form of alignment + * within their groups. * <p> - * A child's ability to stretch is inferred from the alignment properties of - * its row and column groups (which are typically set by setting the - * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters). - * If alignment was defined along a given axis then the component - * is taken as <em>flexible</em> in that direction. If no alignment was set, + * The flexibility of a view is therefore influenced by its alignment which is, + * in turn, typically defined by setting the + * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters. + * If either a weight or alignment were defined along a given axis then the component + * is taken as <em>flexible</em> in that direction. If no weight or alignment was set, * the component is instead assumed to be <em>inflexible</em>. * <p> * Multiple components in the same row or column group are @@ -122,12 +124,16 @@ import static java.lang.Math.min; * elements is flexible if <em>one</em> of its elements is flexible. * <p> * To make a column stretch, make sure all of the components inside it define a - * gravity. To prevent a column from stretching, ensure that one of the components - * in the column does not define a gravity. + * weight or a gravity. To prevent a column from stretching, ensure that one of the components + * in the column does not define a weight or a gravity. * <p> * When the principle of flexibility does not provide complete disambiguation, * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em> - * and <em>bottom</em> edges. + * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout + * parameters as a constraint in the a set of variables that define the grid-lines along a + * given axis. During layout, GridLayout solves the constraints so as to return the unique + * solution to those constraints for which all variables are less-than-or-equal-to + * the corresponding value in any other valid solution. * * <h4>Interpretation of GONE</h4> * @@ -140,18 +146,6 @@ import static java.lang.Math.min; * had never been added to it. * These statements apply equally to rows as well as columns, and to groups of rows or columns. * - * <h5>Limitations</h5> - * - * GridLayout does not provide support for the principle of <em>weight</em>, as defined in - * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible - * to configure a GridLayout to distribute excess space between multiple components. - * <p> - * Some common use-cases may nevertheless be accommodated as follows. - * To place equal amounts of space around a component in a cell group; - * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}). - * For complete control over excess space distribution in a row or column; - * use a {@link LinearLayout} subview to hold the components in the associated cell group. - * When using either of these techniques, bear in mind that cell groups may be defined to overlap. * <p> * See {@link GridLayout.LayoutParams} for a full description of the * layout parameters used by GridLayout. @@ -1018,6 +1012,8 @@ public class GridLayout extends ViewGroup { LayoutParams lp = getLayoutParams(c); if (firstPass) { measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height); + mHorizontalAxis.recordOriginalMeasurement(i); + mVerticalAxis.recordOriginalMeasurement(i); } else { boolean horizontal = (mOrientation == HORIZONTAL); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; @@ -1245,6 +1241,11 @@ public class GridLayout extends ViewGroup { public int[] locations; public boolean locationsValid = false; + public boolean hasWeights; + public boolean hasWeightsValid = false; + public int[] originalMeasurements; + public int[] deltas; + boolean orderPreserved = DEFAULT_ORDER_PRESERVED; private MutableInt parentMin = new MutableInt(0); @@ -1321,7 +1322,10 @@ public class GridLayout extends ViewGroup { // we must include views that are GONE here, see introductory javadoc LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - groupBounds.getValue(i).include(GridLayout.this, c, spec, this); + int size = (spec.weight == 0) ? + getMeasurementIncludingMargin(c, horizontal) : + getOriginalMeasurements()[i] + getDeltas()[i]; + groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size); } } @@ -1693,8 +1697,94 @@ public class GridLayout extends ViewGroup { return trailingMargins; } - private void computeLocations(int[] a) { + private void solve(int[] a) { solve(getArcs(), a); + } + + private boolean computeHasWeights() { + for (int i = 0, N = getChildCount(); i < N; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + if (spec.weight != 0) { + return true; + } + } + return false; + } + + private boolean hasWeights() { + if (!hasWeightsValid) { + hasWeights = computeHasWeights(); + hasWeightsValid = true; + } + return hasWeights; + } + + public int[] getOriginalMeasurements() { + if (originalMeasurements == null) { + originalMeasurements = new int[getChildCount()]; + } + return originalMeasurements; + } + + private void recordOriginalMeasurement(int i) { + if (hasWeights()) { + getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal); + } + } + + public int[] getDeltas() { + if (deltas == null) { + deltas = new int[getChildCount()]; + } + return deltas; + } + + private void shareOutDelta() { + int totalDelta = 0; + float totalWeight = 0; + for (int i = 0, N = getChildCount(); i < N; i++) { + View c = getChildAt(i); + LayoutParams lp = getLayoutParams(c); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + float weight = spec.weight; + if (weight != 0) { + int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i]; + totalDelta += delta; + totalWeight += weight; + } + } + for (int i = 0, N = getChildCount(); i < N; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + float weight = spec.weight; + if (weight != 0) { + int delta = Math.round((weight * totalDelta / totalWeight)); + deltas[i] = delta; + // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end + totalDelta -= delta; + totalWeight -= weight; + } + } + } + + private void solveAndDistributeSpace(int[] a) { + Arrays.fill(getDeltas(), 0); + solve(a); + shareOutDelta(); + arcsValid = false; + forwardLinksValid = false; + backwardLinksValid = false; + groupBoundsValid = false; + solve(a); + } + + private void computeLocations(int[] a) { + if (!hasWeights()) { + solve(a); + } else { + solveAndDistributeSpace(a); + } if (!orderPreserved) { // Solve returns the smallest solution to the constraint system for which all // values are positive. One value is therefore zero - though if the row/col @@ -1777,6 +1867,10 @@ public class GridLayout extends ViewGroup { locations = null; + originalMeasurements = null; + deltas = null; + hasWeightsValid = false; + invalidateValues(); } @@ -1810,6 +1904,9 @@ public class GridLayout extends ViewGroup { * both aspects of alignment within the cell group. It is also possible to specify a child's * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)} * method. + * <p> + * The weight property is also included in Spec and specifies the proportion of any + * excess space that is due to the associated view. * * <h4>WRAP_CONTENT and MATCH_PARENT</h4> * @@ -1851,9 +1948,11 @@ public class GridLayout extends ViewGroup { * <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li> * <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li> * <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li> + * <li>{@link #rowSpec}<code>.weight</code> = 0 </li> * <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li> * <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li> * <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li> + * <li>{@link #columnSpec}<code>.weight</code> = 0 </li> * </ul> * * See {@link GridLayout} for a more complete description of the conventions @@ -1861,8 +1960,10 @@ public class GridLayout extends ViewGroup { * * @attr ref android.R.styleable#GridLayout_Layout_layout_row * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan + * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight * @attr ref android.R.styleable#GridLayout_Layout_layout_column * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan + * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity */ public static class LayoutParams extends MarginLayoutParams { @@ -1889,9 +1990,11 @@ public class GridLayout extends ViewGroup { private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column; private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan; + private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight; private static final int ROW = R.styleable.GridLayout_Layout_layout_row; private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan; + private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight; private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity; @@ -2034,11 +2137,13 @@ public class GridLayout extends ViewGroup { int column = a.getInt(COLUMN, DEFAULT_COLUMN); int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE); - this.columnSpec = spec(column, colSpan, getAlignment(gravity, true)); + float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT); + this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight); int row = a.getInt(ROW, DEFAULT_ROW); int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE); - this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false)); + float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT); + this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight); } finally { a.recycle(); } @@ -2273,10 +2378,9 @@ public class GridLayout extends ViewGroup { return before - a.getAlignmentValue(c, size, gl.getLayoutMode()); } - protected final void include(GridLayout gl, View c, Spec spec, Axis axis) { + protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) { this.flexibility &= spec.getFlexibility(); boolean horizontal = axis.horizontal; - int size = gl.getMeasurementIncludingMargin(c, horizontal); Alignment alignment = gl.getAlignment(spec.alignment, horizontal); // todo test this works correctly when the returned value is UNDEFINED int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode()); @@ -2401,36 +2505,43 @@ public class GridLayout extends ViewGroup { * <li>{@link #spec(int, int)}</li> * <li>{@link #spec(int, Alignment)}</li> * <li>{@link #spec(int, int, Alignment)}</li> + * <li>{@link #spec(int, float)}</li> + * <li>{@link #spec(int, int, float)}</li> + * <li>{@link #spec(int, Alignment, float)}</li> + * <li>{@link #spec(int, int, Alignment, float)}</li> * </ul> * */ public static class Spec { static final Spec UNDEFINED = spec(GridLayout.UNDEFINED); + static final float DEFAULT_WEIGHT = 0; final boolean startDefined; final Interval span; final Alignment alignment; + final float weight; - private Spec(boolean startDefined, Interval span, Alignment alignment) { + private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) { this.startDefined = startDefined; this.span = span; this.alignment = alignment; + this.weight = weight; } - private Spec(boolean startDefined, int start, int size, Alignment alignment) { - this(startDefined, new Interval(start, start + size), alignment); + private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) { + this(startDefined, new Interval(start, start + size), alignment, weight); } final Spec copyWriteSpan(Interval span) { - return new Spec(startDefined, span, alignment); + return new Spec(startDefined, span, alignment, weight); } final Spec copyWriteAlignment(Alignment alignment) { - return new Spec(startDefined, span, alignment); + return new Spec(startDefined, span, alignment, weight); } final int getFlexibility() { - return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH; + return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH; } /** @@ -2478,6 +2589,7 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + size]} </li> * <li> {@code spec.alignment = alignment} </li> + * <li> {@code spec.weight = weight} </li> * </ul> * <p> * To leave the start index undefined, use the value {@link #UNDEFINED}. @@ -2485,9 +2597,55 @@ public class GridLayout extends ViewGroup { * @param start the start * @param size the size * @param alignment the alignment + * @param weight the weight + */ + public static Spec spec(int start, int size, Alignment alignment, float weight) { + return new Spec(start != UNDEFINED, start, size, alignment, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, alignment, weight)}. + * + * @param start the start + * @param alignment the alignment + * @param weight the weight + */ + public static Spec spec(int start, Alignment alignment, float weight) { + return spec(start, 1, alignment, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, default_alignment, weight)} - + * where {@code default_alignment} is specified in + * {@link android.widget.GridLayout.LayoutParams}. + * + * @param start the start + * @param size the size + * @param weight the weight + */ + public static Spec spec(int start, int size, float weight) { + return spec(start, size, UNDEFINED_ALIGNMENT, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, weight)}. + * + * @param start the start + * @param weight the weight + */ + public static Spec spec(int start, float weight) { + return spec(start, 1, weight); + } + + /** + * Equivalent to: {@code spec(start, size, alignment, 0f)}. + * + * @param start the start + * @param size the size + * @param alignment the alignment */ public static Spec spec(int start, int size, Alignment alignment) { - return new Spec(start != UNDEFINED, start, size, alignment); + return spec(start, size, alignment, Spec.DEFAULT_WEIGHT); } /** diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 47ef65a..01e5d40 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -35,8 +35,8 @@ import java.util.Set; /* - * This is used in conjunction with DevicePolicyManager.setForwardingIntents to enable intents to be - * passed in and out of a managed profile. + * This is used in conjunction with the {@link setCrossProfileIntentFilter} method of + * {@link DevicePolicyManager} to enable intents to be passed in and out of a managed profile. */ public class IntentForwarderActivity extends Activity { diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 591267e..183dd05 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -484,8 +484,7 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte mList.clear(); if (mBaseResolveList != null) { - currentResolveList = mBaseResolveList; - mOrigResolveList = null; + currentResolveList = mOrigResolveList = mBaseResolveList; } else { currentResolveList = mOrigResolveList = mPm.queryIntentActivities( mIntent, PackageManager.MATCH_DEFAULT_ONLY diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 1e37fd9..d10451b 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -178,7 +178,7 @@ interface IBackupTransport { /** * Get the data for the application returned by {@link #nextRestorePackage}. * @param data An open, writable file into which the backup data should be stored. - * @return the same error codes as {@link #nextRestorePackage}. + * @return the same error codes as {@link #startRestore}. */ int getRestoreData(in ParcelFileDescriptor outFd); diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 446ef55..f2b29ef 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -18,6 +18,7 @@ package com.android.internal.backup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupTransport; import android.app.backup.RestoreSet; import android.content.ComponentName; import android.content.Context; @@ -47,7 +48,7 @@ import static android.system.OsConstants.*; * later restoring from there. For testing only. */ -public class LocalTransport extends IBackupTransport.Stub { +public class LocalTransport extends BackupTransport { private static final String TAG = "LocalTransport"; private static final boolean DEBUG = true; @@ -217,7 +218,7 @@ public class LocalTransport extends IBackupTransport.Stub { // Restore handling static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; - public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + public RestoreSet[] getAvailableRestoreSets() { long[] existing = new long[POSSIBLE_SETS.length + 1]; int num = 0; diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java index d05699a..77ac313 100644 --- a/core/java/com/android/internal/backup/LocalTransportService.java +++ b/core/java/com/android/internal/backup/LocalTransportService.java @@ -32,6 +32,6 @@ public class LocalTransportService extends Service { @Override public IBinder onBind(Intent intent) { - return sTransport; + return sTransport.getBinder(); } } diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index 495d5c6..df96488 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java @@ -19,6 +19,7 @@ package com.android.internal.inputmethod; import android.content.Context; import android.content.pm.PackageManager; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; @@ -211,54 +212,196 @@ public class InputMethodSubtypeSwitchingController { } } - private final InputMethodSettings mSettings; - private InputMethodAndSubtypeList mSubtypeList; + private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) { + return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, + subtype.hashCode()) : NOT_A_SUBTYPE_ID; + } - @VisibleForTesting - public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList, - boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { - if (imi == null) { - return null; + private static class StaticRotationList { + private final List<ImeSubtypeListItem> mImeSubtypeList; + public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) { + mImeSubtypeList = imeSubtypeList; } - if (imList.size() <= 1) { - return null; + + /** + * Returns the index of the specified input method and subtype in the given list. + * @param imi The {@link InputMethodInfo} to be searched. + * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method + * does not have a subtype. + * @return The index in the given list. -1 if not found. + */ + private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentSubtypeId = calculateSubtypeId(imi, subtype); + final int N = mImeSubtypeList.size(); + for (int i = 0; i < N; ++i) { + final ImeSubtypeListItem isli = mImeSubtypeList.get(i); + // Skip until the current IME/subtype is found. + if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) { + return i; + } + } + return -1; } - // Here we have two rotation groups, depending on the returned boolean value of - // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}. - final boolean expectedValueOfSupportsSwitchingToNextInputMethod = - imi.supportsSwitchingToNextInputMethod(); - final int N = imList.size(); - final int currentSubtypeId = - subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, - subtype.hashCode()) : NOT_A_SUBTYPE_ID; - for (int i = 0; i < N; ++i) { - final ImeSubtypeListItem isli = imList.get(i); - // Skip until the current IME/subtype is found. - if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) { - continue; + + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, + InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return null; } - // Found the current IME/subtype. Start searching the next IME/subtype from here. - for (int j = 0; j < N - 1; ++j) { - final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); - // Skip if the candidate doesn't belong to the expected rotation group. - if (expectedValueOfSupportsSwitchingToNextInputMethod != - candidate.mImi.supportsSwitchingToNextInputMethod()) { - continue; - } + if (mImeSubtypeList.size() <= 1) { + return null; + } + final int currentIndex = getIndex(imi, subtype); + if (currentIndex < 0) { + return null; + } + final int N = mImeSubtypeList.size(); + for (int offset = 1; offset < N; ++offset) { + // Start searching the next IME/subtype from the next of the current index. + final int candidateIndex = (currentIndex + offset) % N; + final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex); // Skip if searching inside the current IME only, but the candidate is not // the current IME. - if (onlyCurrentIme && !candidate.mImi.equals(imi)) { + if (onlyCurrentIme && !imi.equals(candidate.mImi)) { continue; } return candidate; } - // No appropriate IME/subtype is found in the list. Give up. return null; } - // The current IME/subtype is not found in the list. Give up. - return null; } + private static class DynamicRotationList { + private static final String TAG = DynamicRotationList.class.getSimpleName(); + private final List<ImeSubtypeListItem> mImeSubtypeList; + private final int[] mUsageHistoryOfSubtypeListItemIndex; + + public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) { + mImeSubtypeList = imeSubtypeListItems; + mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()]; + final int N = mImeSubtypeList.size(); + for (int i = 0; i < N; i++) { + mUsageHistoryOfSubtypeListItemIndex[i] = i; + } + } + + /** + * Returns the index of the specified object in + * {@link #mUsageHistoryOfSubtypeListItemIndex}. + * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank" + * so as not to be confused with the index in {@link #mImeSubtypeList}. + * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually. + */ + private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentSubtypeId = calculateSubtypeId(imi, subtype); + final int N = mUsageHistoryOfSubtypeListItemIndex.length; + for (int usageRank = 0; usageRank < N; usageRank++) { + final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank]; + final ImeSubtypeListItem subtypeListItem = + mImeSubtypeList.get(subtypeListItemIndex); + if (subtypeListItem.mImi.equals(imi) && + subtypeListItem.mSubtypeId == currentSubtypeId) { + return usageRank; + } + } + // Not found in the known IME/Subtype list. + return -1; + } + + public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentUsageRank = getUsageRank(imi, subtype); + // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0 + if (currentUsageRank <= 0) { + return; + } + final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank]; + System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0, + mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank); + mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex; + } + + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, + InputMethodInfo imi, InputMethodSubtype subtype) { + int currentUsageRank = getUsageRank(imi, subtype); + if (currentUsageRank < 0) { + if (DEBUG) { + Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype); + } + return null; + } + final int N = mUsageHistoryOfSubtypeListItemIndex.length; + for (int i = 1; i < N; i++) { + final int subtypeListItemRank = (currentUsageRank + i) % N; + final int subtypeListItemIndex = + mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank]; + final ImeSubtypeListItem subtypeListItem = + mImeSubtypeList.get(subtypeListItemIndex); + if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) { + continue; + } + return subtypeListItem; + } + return null; + } + } + + @VisibleForTesting + public static class ControllerImpl { + // TODO: Switch to DynamicRotationList for smarter rotation. + private final StaticRotationList mSwitchingAwareSubtypeList; + private final StaticRotationList mSwitchingUnawareSubtypeList; + + public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) { + mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, + true /* supportsSwitchingToNextInputMethod */)); + mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, + false /* supportsSwitchingToNextInputMethod */)); + } + + public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi, + InputMethodSubtype subtype) { + if (imi == null) { + return null; + } + if (imi.supportsSwitchingToNextInputMethod()) { + return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } else { + return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } + } + + public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return; + } + // TODO: Enable the following code when DynamicRotationList is enabled. + // if (imi.supportsSwitchingToNextInputMethod()) { + // mSwitchingAwareSubtypeList.onUserAction(imi, subtype); + // } + } + + private static List<ImeSubtypeListItem> filterImeSubtypeList( + final List<ImeSubtypeListItem> items, + final boolean supportsSwitchingToNextInputMethod) { + final ArrayList<ImeSubtypeListItem> result = new ArrayList<>(); + final int ALL_ITEMS_COUNT = items.size(); + for (int i = 0; i < ALL_ITEMS_COUNT; i++) { + final ImeSubtypeListItem item = items.get(i); + if (item.mImi.supportsSwitchingToNextInputMethod() == + supportsSwitchingToNextInputMethod) { + result.add(item); + } + } + return result; + } + } + + private final InputMethodSettings mSettings; + private InputMethodAndSubtypeList mSubtypeList; + private ControllerImpl mController; + private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) { mSettings = settings; resetCircularListLocked(context); @@ -269,19 +412,30 @@ public class InputMethodSubtypeSwitchingController { return new InputMethodSubtypeSwitchingController(settings, context); } - // TODO: write unit tests for this method and the logic that determines the next subtype public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) { - // TODO: Implement this. + if (mController == null) { + if (DEBUG) { + Log.e(TAG, "mController shouldn't be null."); + } + return; + } + mController.onUserActionLocked(imi, subtype); } public void resetCircularListLocked(Context context) { mSubtypeList = new InputMethodAndSubtypeList(context, mSettings); + mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList()); } - public ImeSubtypeListItem getNextInputMethodLocked( - boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { - return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(), - onlyCurrentIme, imi, subtype); + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, + InputMethodSubtype subtype) { + if (mController == null) { + if (DEBUG) { + Log.e(TAG, "mController shouldn't be null."); + } + return null; + } + return mController.getNextInputMethod(onlyCurrentIme, imi, subtype); } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes, diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 7aa241a..928a7f8 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -128,8 +128,7 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, int sampleSize, bool ditherImage) { - SkImageInfo bitmapInfo; - if (!bitmap->asImageInfo(&bitmapInfo)) { + if (kUnknown_SkColorType == bitmap->colorType()) { ALOGW("bitmap has unknown configuration so no memory has been allocated"); return NULL; } @@ -137,9 +136,9 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, SkImageRef* pr; // only use ashmem for large images, since mmaps come at a price if (bitmap->getSize() >= 32 * 1024) { - pr = new SkImageRef_ashmem(bitmapInfo, stream, sampleSize); + pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize); } else { - pr = new SkImageRef_GlobalPool(bitmapInfo, stream, sampleSize); + pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize); } pr->setDitherImage(ditherImage); bitmap->setPixelRef(pr)->unref(); diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp index fbfa2ec..3275875 100644 --- a/core/jni/android/graphics/DrawFilter.cpp +++ b/core/jni/android/graphics/DrawFilter.cpp @@ -30,6 +30,35 @@ namespace android { +// Custom version of SkPaintFlagsDrawFilter that also calls setFilterLevel. +class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter { +public: + CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags, + SkPaint::FilterLevel desiredLevel) + : SkPaintFlagsDrawFilter(clearFlags, setFlags) + , fDesiredLevel(desiredLevel) { + } + + virtual bool filter(SkPaint* paint, Type type) { + SkPaintFlagsDrawFilter::filter(paint, type); + paint->setFilterLevel(fDesiredLevel); + return true; + } + +private: + const SkPaint::FilterLevel fDesiredLevel; +}; + +// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it. +static inline bool hadFiltering(jint& flags) { + // Equivalent to the Java Paint's FILTER_BITMAP_FLAG. + static const uint32_t sFilterBitmapFlag = 0x02; + + const bool result = (flags & sFilterBitmapFlag) != 0; + flags &= ~sFilterBitmapFlag; + return result; +} + class SkDrawFilterGlue { public: @@ -40,12 +69,25 @@ public: static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz, jint clearFlags, jint setFlags) { - // trim off any out-of-range bits - clearFlags &= SkPaint::kAllFlags; - setFlags &= SkPaint::kAllFlags; - if (clearFlags | setFlags) { - SkDrawFilter* filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags); + // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no + // longer has a Skia equivalent flag (instead it corresponds to + // calling setFilterLevel), and keep track of which group(s), if + // any, had the flag set. + const bool turnFilteringOn = hadFiltering(setFlags); + const bool turnFilteringOff = hadFiltering(clearFlags); + + SkDrawFilter* filter; + if (turnFilteringOn) { + // Turning filtering on overrides turning it off. + filter = new CompatFlagsDrawFilter(clearFlags, setFlags, + SkPaint::kLow_FilterLevel); + } else if (turnFilteringOff) { + filter = new CompatFlagsDrawFilter(clearFlags, setFlags, + SkPaint::kNone_FilterLevel); + } else { + filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags); + } return reinterpret_cast<jlong>(filter); } else { return NULL; diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp index 2113330..b394905 100644 --- a/core/jni/android/graphics/MaskFilter.cpp +++ b/core/jni/android/graphics/MaskFilter.cpp @@ -21,8 +21,7 @@ public: static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) { SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); - SkMaskFilter* filter = SkBlurMaskFilter::Create( - (SkBlurMaskFilter::BlurStyle)blurStyle, sigma); + SkMaskFilter* filter = SkBlurMaskFilter::Create((SkBlurStyle)blurStyle, sigma); ThrowIAE_IfNull(env, filter); return reinterpret_cast<jlong>(filter); } diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index fcf8f83..ddcc396 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -51,6 +51,8 @@ struct SensorOffsets jfieldID fifoMaxEventCount; jfieldID stringType; jfieldID requiredPermission; + jfieldID maxDelay; + jfieldID isWakeUpSensor; } gSensorOffsets; @@ -78,6 +80,8 @@ nativeClassInit (JNIEnv *_env, jclass _this) sensorOffsets.stringType = _env->GetFieldID(sensorClass, "mStringType", "Ljava/lang/String;"); sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission", "Ljava/lang/String;"); + sensorOffsets.maxDelay = _env->GetFieldID(sensorClass, "mMaxDelay", "I"); + sensorOffsets.isWakeUpSensor = _env->GetFieldID(sensorClass, "mWakeUpSensor", "Z"); } static jint @@ -112,6 +116,8 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) env->SetObjectField(sensor, sensorOffsets.stringType, stringType); env->SetObjectField(sensor, sensorOffsets.requiredPermission, requiredPermission); + env->SetIntField(sensor, sensorOffsets.maxDelay, list->getMaxDelay()); + env->SetBooleanField(sensor, sensorOffsets.isWakeUpSensor, list->isWakeUpSensor()); next++; return size_t(next) < count ? next : 0; } @@ -160,7 +166,8 @@ private: ASensorEvent buffer[16]; while ((n = q->read(buffer, 16)) > 0) { for (int i=0 ; i<n ; i++) { - if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) { + if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER || + buffer[i].type == SENSOR_TYPE_WAKE_UP_STEP_COUNTER) { // step-counter returns a uint64, but the java API only deals with floats float value = float(buffer[i].u64.step_counter); env->SetFloatArrayRegion(mScratch, 0, 1, &value); @@ -175,11 +182,26 @@ private: gBaseEventQueueClassInfo.dispatchFlushCompleteEvent, buffer[i].meta_data.sensor); } else { + int8_t status; + switch (buffer[i].type) { + case SENSOR_TYPE_ORIENTATION: + case SENSOR_TYPE_MAGNETIC_FIELD: + case SENSOR_TYPE_ACCELEROMETER: + case SENSOR_TYPE_GYROSCOPE: + status = buffer[i].vector.status; + break; + case SENSOR_TYPE_HEART_RATE: + status = buffer[i].heart_rate.status; + break; + default: + status = SENSOR_STATUS_ACCURACY_HIGH; + break; + } env->CallVoidMethod(mReceiverObject, gBaseEventQueueClassInfo.dispatchSensorEvent, buffer[i].sensor, mScratch, - buffer[i].vector.status, + status, buffer[i].timestamp); } if (env->ExceptionCheck()) { diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index c5dd06f..9141d44 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -373,7 +373,7 @@ static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject clazz, jlong rendererPtr, jlong matrixPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); - renderer->setMatrix(matrix); + renderer->setMatrix(matrix ? *matrix : SkMatrix::I()); } static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject clazz, @@ -387,7 +387,7 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz, jlong rendererPtr, jlong matrixPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); - renderer->concatMatrix(matrix); + renderer->concatMatrix(*matrix); } // ---------------------------------------------------------------------------- @@ -430,7 +430,7 @@ static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject claz OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); - renderer->drawBitmap(bitmap, matrix, paint); + renderer->drawBitmap(bitmap, *matrix, paint); } static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz, diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 6ae02e0..a590dbf 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -211,8 +211,9 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); - uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits); - if (bits) { + BitSet64 bits = + BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits)); + if (!bits.isEmpty()) { jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues)); if (valuesArray) { @@ -221,11 +222,9 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, uint32_t index = 0; do { - uint32_t axis = __builtin_ctzll(bits); - uint64_t axisBit = 1LL << axis; - bits &= ~axisBit; + uint32_t axis = bits.clearFirstMarkedBit(); outRawPointerCoords->setAxisValue(axis, values[index++]); - } while (bits); + } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT); env->DeleteLocalRef(valuesArray); @@ -275,21 +274,19 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation, rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - const uint64_t unpackedAxisBits = 0 - | (1LL << AMOTION_EVENT_AXIS_X) - | (1LL << AMOTION_EVENT_AXIS_Y) - | (1LL << AMOTION_EVENT_AXIS_PRESSURE) - | (1LL << AMOTION_EVENT_AXIS_SIZE) - | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR) - | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR) - | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR) - | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR) - | (1LL << AMOTION_EVENT_AXIS_ORIENTATION); - uint64_t outBits = 0; - uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits; - if (remainingBits) { - uint32_t packedAxesCount = __builtin_popcountll(remainingBits); + BitSet64 bits = BitSet64(rawPointerCoords->bits); + bits.clearBit(AMOTION_EVENT_AXIS_X); + bits.clearBit(AMOTION_EVENT_AXIS_Y); + bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE); + bits.clearBit(AMOTION_EVENT_AXIS_SIZE); + bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR); + bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION); + if (!bits.isEmpty()) { + uint32_t packedAxesCount = bits.count(); jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount, outPointerCoordsObj); if (!outValuesArray) { @@ -302,12 +299,10 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer const float* values = rawPointerCoords->values; uint32_t index = 0; do { - uint32_t axis = __builtin_ctzll(remainingBits); - uint64_t axisBit = 1LL << axis; - remainingBits &= ~axisBit; - outBits |= axisBit; + uint32_t axis = bits.clearFirstMarkedBit(); + outBits |= BitSet64::valueForBit(axis); outValues[index++] = rawPointerCoords->getAxisValue(axis); - } while (remainingBits); + } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0); env->DeleteLocalRef(outValuesArray); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 5a935a9..4594cc3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -61,6 +61,13 @@ static struct { jfieldID secure; } gPhysicalDisplayInfoClassInfo; +static struct { + jfieldID bottom; + jfieldID left; + jfieldID right; + jfieldID top; +} gRectClassInfo; + // Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref. void DeleteScreenshot(void* addr, void* context) { SkASSERT(addr == ((ScreenshotClient*) context)->getPixels()); @@ -104,25 +111,32 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) { ctrl->decStrong((void *)nativeCreate); } -static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj, - jint width, jint height, jint minLayer, jint maxLayer, bool allLayers, - bool useIdentityTransform) { +static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, + jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, + jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken == NULL) { return NULL; } + int left = env->GetIntField(sourceCropObj, gRectClassInfo.left); + int top = env->GetIntField(sourceCropObj, gRectClassInfo.top); + int right = env->GetIntField(sourceCropObj, gRectClassInfo.right); + int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom); + Rect sourceCrop(left, top, right, bottom); + ScreenshotClient* screenshot = new ScreenshotClient(); status_t res; if (width > 0 && height > 0) { if (allLayers) { - res = screenshot->update(displayToken, width, height, useIdentityTransform); - } else { - res = screenshot->update(displayToken, width, height, minLayer, maxLayer, + res = screenshot->update(displayToken, sourceCrop, width, height, useIdentityTransform); + } else { + res = screenshot->update(displayToken, sourceCrop, width, height, + minLayer, maxLayer, useIdentityTransform); } } else { - res = screenshot->update(displayToken, useIdentityTransform); + res = screenshot->update(displayToken, sourceCrop, useIdentityTransform); } if (res != NO_ERROR) { delete screenshot; @@ -174,20 +188,25 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject display GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL); } -static void nativeScreenshot(JNIEnv* env, jclass clazz, - jobject displayTokenObj, jobject surfaceObj, - jint width, jint height, jint minLayer, jint maxLayer, bool allLayers, - bool useIdentityTransform) { +static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, + jobject surfaceObj, jobject sourceCropObj, jint width, jint height, + jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken != NULL) { sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj); if (consumer != NULL) { + int left = env->GetIntField(sourceCropObj, gRectClassInfo.left); + int top = env->GetIntField(sourceCropObj, gRectClassInfo.top); + int right = env->GetIntField(sourceCropObj, gRectClassInfo.right); + int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom); + Rect sourceCrop(left, top, right, bottom); + if (allLayers) { minLayer = 0; maxLayer = -1; } - ScreenshotClient::capture( - displayToken, consumer->getIGraphicBufferProducer(), + ScreenshotClient::capture(displayToken, + consumer->getIGraphicBufferProducer(), sourceCrop, width, height, uint32_t(minLayer), uint32_t(maxLayer), useIdentityTransform); } @@ -563,9 +582,9 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeRelease }, {"nativeDestroy", "(J)V", (void*)nativeDestroy }, - {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZZ)Landroid/graphics/Bitmap;", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZ)Landroid/graphics/Bitmap;", (void*)nativeScreenshotBitmap }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZZ)V", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V", (void*)nativeScreenshot }, {"nativeOpenTransaction", "()V", (void*)nativeOpenTransaction }, @@ -640,6 +659,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F"); gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z"); + jclass rectClazz = env->FindClass("android/graphics/Rect"); + gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I"); + gRectClassInfo.left = env->GetFieldID(rectClazz, "left", "I"); + gRectClassInfo.right = env->GetFieldID(rectClazz, "right", "I"); + gRectClassInfo.top = env->GetFieldID(rectClazz, "top", "I"); + jclass frameStatsClazz = env->FindClass("android/view/FrameStats"); jfieldID undefined_time_nano_field = env->GetStaticFieldID(frameStatsClazz, "UNDEFINED_TIME_NANO", "J"); nsecs_t undefined_time_nano = env->GetStaticLongField(frameStatsClazz, undefined_time_nano_field); diff --git a/core/res/res/anim/slide_in_micro.xml b/core/res/res/anim/slide_in_micro.xml new file mode 100644 index 0000000..6320e80 --- /dev/null +++ b/core/res/res/anim/slide_in_micro.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/res/anim/slide_in_micro.xml +** +** Copyright 2014, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate android:fromXDelta="100%p" android:toXDelta="0" + android:duration="@android:integer/config_mediumAnimTime"/> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml new file mode 100644 index 0000000..4cb6df0 --- /dev/null +++ b/core/res/res/anim/slide_out_micro.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/res/anim/slide_out_micro.xml +** +** Copyright 2014, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate android:fromXDelta="0" android:toXDelta="100%p" + android:duration="@android:integer/config_mediumAnimTime"/> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index df59abe..b17b8c6 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -267,7 +267,7 @@ <string name="permdesc_statusBarService" msgid="716113660795976060">"Tillader, at appen er statusbjælken."</string> <string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string> <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillader, at appen kan udvide og skjule statusbjælken."</string> - <string name="permlab_install_shortcut" msgid="4279070216371564234">"installer genveje"</string> + <string name="permlab_install_shortcut" msgid="4279070216371564234">"installere genveje"</string> <string name="permdesc_install_shortcut" msgid="8341295916286736996">"Tillader, at en applikation føjer genveje til startskærmen uden brugerindgriben."</string> <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"afinstaller genveje"</string> <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillader, at applikationen fjerner genveje på startskærmen uden brugerindgriben."</string> @@ -281,7 +281,7 @@ <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Tillader, at appen kan modtage og behandle nødtransmissioner. Denne tilladelse er kun tilgængelig for systemapps."</string> <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"læse Cell Broadcast-beskeder"</string> <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser Cell Broadcast-beskeder, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-beskeder for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af din enhed, når der modtages en Cell Broadcast-besked om en nødsituation."</string> - <string name="permlab_sendSms" msgid="5600830612147671529">"send sms-beskeder"</string> + <string name="permlab_sendSms" msgid="5600830612147671529">"sende sms-beskeder"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string> <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send hændelser, hvor der skal svares pr. besked"</string> <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillader, at appen kan sende anmodninger til andre apps til beskeder for at håndtere hændelser, hvor der skal svares pr. besked."</string> @@ -295,7 +295,7 @@ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillader, at appen kan modtage og behandle WAP-beskeder. Denne tilladelse omfatter muligheden for at overvåge eller slette de beskeder, der sendes til dig, uden at vise dem til dig."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string> <string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges på enheden."</string> - <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommuniker på tværs af brugere"</string> + <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommunikere på tværs af brugere"</string> <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillader, at appen udfører handlinger på tværs af forskellige brugere på enheden. Ondsindede apps kan bruge dette til at krænke beskyttelsen mellem brugere."</string> <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fuld licens til at kommunikere på tværs af brugere"</string> <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Tillader alle mulige former for kommunikation på tværs af brugere."</string> @@ -325,7 +325,7 @@ <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"Tillader, at appen kan tvinge andre apps til at stoppe."</string> <string name="permlab_forceBack" msgid="652935204072584616">"tvinge appen til at lukke"</string> <string name="permdesc_forceBack" msgid="3892295830419513623">"Tillader, at appen kan tvinge enhver aktivitet i forgrunden til at lukke og gå tilbage. Bør aldrig være nødvendigt til almindelige apps."</string> - <string name="permlab_dump" msgid="1681799862438954752">"hent intern systemtilstand"</string> + <string name="permlab_dump" msgid="1681799862438954752">"hente intern systemtilstand"</string> <string name="permdesc_dump" msgid="1778299088692290329">"Tillader, at appen kan hente systemets interne tilstand. Ondsindede apps kan hente en lang række fortrolige og beskyttede oplysninger, som de normalt aldrig ville have brug for."</string> <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"hente skærmindhold"</string> <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"Tillader, at appen kan hente indholdet i det aktive vindue. Ondsindede apps kan hente al indholdet i vinduet og undersøge al dens tekst med undtagelse af adgangskoder."</string> @@ -359,7 +359,7 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string> - <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hent statistikker for handlinger i appen"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hente statistikker om handlinger i appen"</string> <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillader, at appen indhenter statistikker for handlinger i applikationen. Denne handling bruges ikke i almindelige apps."</string> <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string> <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string> @@ -474,7 +474,7 @@ <string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string> <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en e-mail eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en e-mail til dem eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string> - <string name="permlab_readCallLog" msgid="3478133184624102739">"læs opkaldsliste"</string> + <string name="permlab_readCallLog" msgid="3478133184624102739">"læse opkaldsliste"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillader, at appen kan læse din tablets opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele opkaldslistedata uden din viden."</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillader, at appen kan læse telefonens opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele disse opkaldslistedata uden din viden."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv opkaldsliste"</string> @@ -486,9 +486,9 @@ <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Tillader, at appen kan ændre eller tilføje oplysninger i din personlige profil, der er gemt på din enhed, f.eks. dit navn eller dine kontaktoplysninger. Dette betyder, at andre apps kan identificere dig og sende profiloplysninger til andre."</string> <string name="permlab_bodySensors" msgid="4871091374767171066">"kropssensorer (f.eks. pulsmålere)"</string> <string name="permdesc_bodySensors" product="default" msgid="2998865085124153531">"Tillader, at appen får adgang til data fra sensorer, du bruger til at måle, hvad der sker inde i din krop, f.eks. din puls."</string> - <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læs din sociale strøm"</string> + <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læse din sociale strøm"</string> <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Tillader, at appen kan få adgang til og synkronisere sociale opdateringer fra dig og dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen læse kommunikation mellem dig og dine venner på sociale netværk, uanset fortrolighed. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string> - <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skriv i din sociale strøm"</string> + <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skrive i din sociale strøm"</string> <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Tillader, at appen kan vise sociale opdateringer fra dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen producere meddelelser, der kan synes at komme fra en ven. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"læse kalenderbegivenheder og fortrolige oplysninger"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillader, at appen kan læse alle de kalenderbegivenheder, der er gemt på din tablet, f.eks. venners eller kollegers. Med denne tilladelse kan appen dele eller gemme dine kalenderdata, uanset fortrolighed eller følsomhed."</string> @@ -526,7 +526,7 @@ <string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"Tillader, at appen opfanger og omdirigerer et sikkert videooutput."</string> <string name="permlab_mediaContentControl" msgid="8749790560720562511">"kontrollér medieafspilning og metadataadgang"</string> <string name="permdesc_mediaContentControl" msgid="1637478200272062">"Tillader, at appen styrer medieafspilning og får adgang til medieoplysninger (titel, forfatter...)."</string> - <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string> + <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string> <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string> <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string> <string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillader, at appen kan optage lyd med mikrofonen. Med denne tilladelse kan appen til enhver tid optage lyd uden din bekræftelse."</string> @@ -632,7 +632,7 @@ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillader, at appen kan oprette netværkssockets og bruge tilpassede netværksprotokoller. Browseren og andre applikationer indeholder midler til at sende data til internettet, så med denne tilladelse er der ingen forpligtelse til at sende data til internettet."</string> <string name="permlab_writeApnSettings" msgid="505660159675751896">"ændre/opfange netværksindstillinger og trafik"</string> <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"Tillader, at appen kan ændre netværksindstillinger og opsnappe og inspicere al netværkstrafik, f.eks. for at ændre proxy og port for et adgangspunkt. Ondsindede apps kan overvåge, omdirigere eller ændre netværkspakker uden din viden."</string> - <string name="permlab_changeNetworkState" msgid="958884291454327309">"skift netværksforbindelse"</string> + <string name="permlab_changeNetworkState" msgid="958884291454327309">"skifte netværksforbindelse"</string> <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillader, at appen kan ændre netværksforbindelsens tilstand."</string> <string name="permlab_changeTetherState" msgid="5952584964373017960">"skifte forbindelse til netdeling"</string> <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Tillader, at appen kan ændre tilstand for en netværksforbindelse via netdeling."</string> @@ -672,9 +672,9 @@ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillader, at en app kan ændre synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. anvendes til at aktivere synkronisering af appen Personer med en konto."</string> <string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string> <string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillader, at en app kan læse synkroniseringsstatistikkerne for en konto, f.eks. historikken for synkroniserede begivenheder og hvor meget data der synkroniseres."</string> - <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læs abonnerede feeds"</string> + <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læse feeds, jeg abonnerer på"</string> <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string> - <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skriv abonnerede feeds"</string> + <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skrive feeds, som jeg abonnerer på"</string> <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Tillader, at appen kan ændre dine synkroniserede feeds. Ondsindede apps kan ændre dine synkroniserede feeds."</string> <string name="permlab_readDictionary" msgid="4107101525746035718">"læse termer, som du har føjet til ordbogen"</string> <string name="permdesc_readDictionary" msgid="659614600338904243">"Tillader, at appen kan læse alle ord, navne og sætninger, som brugeren har gemt i brugerordbogen."</string> @@ -995,7 +995,7 @@ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillader, at appen kan ændre browserens historik eller de bogmærker, der er gemt på din telefon. Dette kan give appen tilladelse til at slette eller ændre browserdata. Bemærk! Denne tilladelse håndhæves muligvis ikke af tredjepartsbrowsere eller andre applikationer med websøgningsfunktioner."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"indstille en alarm"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string> - <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføj telefonsvarer"</string> + <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string> <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string> <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string> <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string> @@ -1275,7 +1275,7 @@ <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angiv dato"</string> <string name="date_time_set" msgid="5777075614321087758">"Angiv"</string> <string name="date_time_done" msgid="2507683751759308828">"Udført"</string> - <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NYHED! "</font></string> + <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NY: "</font></string> <string name="perms_description_app" msgid="5139836143293299417">"Leveret af <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string> <string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste dig penge"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 989eec6..d0561b9 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1471,8 +1471,8 @@ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Επιλέξτε κάποια εφαρμογή"</string> <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</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="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> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string> <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string> @@ -1516,7 +1516,7 @@ <string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string> <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string> <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string> - <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string> + <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string> <string name="list_delimeter" msgid="3975117572185494152">", "</string> <string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string> <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index c8eca25..3d043c4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -294,7 +294,7 @@ <string name="permlab_receiveWapPush" msgid="5991398711936590410">"recevoir des messages texte (WAP)"</string> <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet à l\'application de recevoir et de traiter les messages WAP. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les applications en cours d\'exécution"</string> - <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible de d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string> + <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string> <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre les utilisateurs"</string> <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet à l\'application d\'effectuer des actions entre les différents utilisateurs de l\'appareil. Les applications malveillantes peuvent utiliser cette autorisation pour passer outre la protection entre les utilisateurs."</string> <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"autorisation totale d\'interagir entre les utilisateurs"</string> @@ -503,7 +503,7 @@ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autoriser l\'installation d\'un fournisseur de services de localisation"</string> <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Permet de créer des sources de localisation fictives à des fins de tests ou pour installer un nouveau fournisseur de position. L\'application peut ainsi modifier la position et/ou l\'état renvoyé par d\'autres sources de localisation telles que le GPS ou les fournisseurs de position."</string> <string name="permlab_accessFineLocation" msgid="1191898061965273372">"position précise (GPS et réseau)"</string> - <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puissent déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string> + <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string> <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"position approximative (réseau)"</string> <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string> <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Accès à SurfaceFlinger"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 000fb3a..2adaaf1 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -496,7 +496,7 @@ <string name="permlab_writeCalendar" msgid="8438874755193825647">"dodajte ili izmijenite kalendarske događaje i pošaljite e-poštu gostima bez znanja vlasnika"</string> <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na tabletnom računalu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string> <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na telefonu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string> - <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogući testiranje izvora lokacije"</string> + <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogućeno testiranje izvora lokacije"</string> <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Stvaranje lažnih izvora lokacije radi testiranja ili za instaliranje novog pružatelja usluga lokacije. To aplikaciji omogućuje zaobilaženje lokacije i/ili statusa koji vraćaju drugi izvori lokacije, primjerice GPS ili pružatelji usluga lokacije."</string> <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string> <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Aplikaciji omogućuje pristup dodatnim naredbama za pružatelja usluga lokacije. To aplikaciji može omogućiti ometanje rada GPS-a ili drugih izvora lokacije."</string> diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml index dbe2d7f..f3b8495 100644 --- a/core/res/res/values-km-rKH/strings.xml +++ b/core/res/res/values-km-rKH/strings.xml @@ -68,7 +68,7 @@ </plurals> <string name="imei" msgid="2625429890869005782">"IMEI"</string> <string name="meid" msgid="4841221237681254195">"MEID"</string> - <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string> + <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string> <string name="ClirMmi" msgid="7784673673446833091">"លេខសម្គាល់អ្នកហៅចេញ"</string> <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូនការហៅបន្ត"</string> <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំការហៅ"</string> @@ -125,7 +125,7 @@ <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string> <string name="fcComplete" msgid="3118848230966886575">"កូដលក្ខណៈពេញលេញ។"</string> <string name="fcError" msgid="3327560126588500777">"បញ្ហាការតភ្ជាប់ ឬកូដលក្ខណៈមិនត្រឹមត្រូវ។"</string> - <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string> + <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string> <string name="httpError" msgid="7956392511146698522">"មានកំហុសបណ្ដាញ។"</string> <string name="httpErrorLookup" msgid="4711687456111963163">"រកមិនឃើញ URL ។"</string> <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍ផ្ទៀងផ្ទាត់តំបន់បណ្ដាញមិនត្រូវបានគាំទ្រ។"</string> @@ -183,7 +183,7 @@ <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើកសំឡេង"</string> <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string> <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string> - <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string> + <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string> <string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string> <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string> @@ -195,7 +195,7 @@ <string name="permgrouplab_messages" msgid="7521249148445456662">"សាររបស់អ្នក"</string> <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និងសរសេរសារ SMS, អ៊ីមែល និងសារផ្សេងៗទៀតរបស់អ្នក។"</string> <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នក"</string> - <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string> + <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string> <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មានសង្គមរបស់អ្នក"</string> <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីទំនាក់ទំនង និងការភ្ជាប់សង្គមរបស់អ្នក។"</string> <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំងរបស់អ្នក"</string> @@ -384,7 +384,7 @@ <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យកម្មវិធីមើលគ្រាប់ចុចដែលអ្នកចុចពេលមានអន្តរកម្មជាមួយកម្មវិធីផ្សេង (ដូចជា បញ្ចូលពាក្យសម្ងាត់)។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចងទៅវិធីសាស្ត្របញ្ចូល"</string> <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃវិធីសាស្ត្របញ្ចូល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string> + <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មភាពងាយស្រួល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចងសេវាកម្មបោះពុម្ព"</string> <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -402,7 +402,7 @@ <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលរបស់សេវាកម្មអន្តរកម្មសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string> <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string> + <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string> <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់ទៅសេវាកម្មក្រុមហ៊ុនផ្ដល់ច្រក"</string> <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅក្រុមហ៊ុនផ្ដល់ច្រកដែលបានចុះឈ្មោះ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -410,7 +410,7 @@ <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string> <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតខ្ពស់នៃការបញ្ចូលទូរទស្សន៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string> + <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string> <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាតឲ្យម្ចាស់បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍សកម្មចេញ។ មិនគួរប្រើសម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរទិសអេក្រង់"</string> <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យកម្មវិធីប្ដូរការបង្វិលអេក្រង់នៅពេលណាមួយ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -422,9 +422,9 @@ <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យកម្មវិធីស្នើសញ្ញាដែលបានផ្ដល់ត្រូវផ្ញើទៅដំណើរការស្ថិតស្ថេរទាំងអស់។"</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើឲ្យកម្មវិធីដំណើរការជានិច្ច"</string> <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យកម្មវិធីធ្វើជាផ្នែកស្ថិតស្ថេរដោយខ្លួនឯងក្នុងអង្គចងចាំ។ វាអាចកំណត់អង្គចងចាំដែលអាចប្រើបានចំពោះកម្មវិធីផ្សេងៗ ដោយធ្វើឲ្យកុំព្យូទ័របន្ទះយឺត។"</string> - <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string> + <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string> <string name="permlab_deletePackages" msgid="184385129537705938">"លុបកម្មវិធី"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។ "</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> @@ -475,7 +475,7 @@ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងកុំព្យូទ័របន្ទះ រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនងជាក់លាក់។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនងរបស់អ្នក។"</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬបានទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនាក់ជាក់លាក់។ សិទ្ធិនេះឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនង។"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"អានកំណត់ហេតុហៅ"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យកម្មវិធីអានបញ្ជីហៅទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរបញ្ជីហៅ"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string> @@ -609,7 +609,7 @@ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យកម្មវិធីកំណត់ជំនួយទំហំផ្ទាំងរូបភាពប្រព័ន្ធ។"</string> <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់ប្រព័ន្ធទៅលំនាំដើមរោងចក្រឡើងវិញ"</string> <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យកម្មវិធីកំណត់ប្រព័ន្ធដូចការកំណត់ចេញពីរោងចក្រឡើងវិញពេញលេញ ដោយលុបទិន្នន័យ ការកំណត់រចនាសម្ព័ន្ធ និងកម្មវិធីបានដំឡើង។"</string> - <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</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="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់តំបន់ពេលវេលា"</string> @@ -775,7 +775,7 @@ <string-array name="organizationTypes"> <item msgid="7546335612189115615">"កន្លែងធ្វើការ"</item> <item msgid="4378074129049520373">"ផ្សេងៗ"</item> - <item msgid="3455047468583965104">"តាមតម្រូវការ"</item> + <item msgid="3455047468583965104">"តាមតម្រូវការ"</item> </string-array> <string-array name="imProtocols"> <item msgid="8595261363518459565">"AIM"</item> @@ -791,7 +791,7 @@ <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string> <string name="phoneTypeMobile" msgid="6501463557754751037">"ចល័ត"</string> <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែងធ្វើការ"</string> - <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string> + <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string> <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារផ្ទះ"</string> <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string> @@ -916,7 +916,7 @@ <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាមលំនាំច្រើនពេក"</string> <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បីដោះសោ ចូលគណនី Google របស់អ្នក។"</string> <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះអ្នកប្រើ (អ៊ីមែល)"</string> - <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string> + <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string> <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string> <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string> <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -961,7 +961,7 @@ <string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string> <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string> <string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string> - <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string> + <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string> <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រមានចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string> <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string> <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់ការរុករក"</string> @@ -1019,7 +1019,7 @@ <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string> <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string> <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string> - <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string> + <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string> <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string> <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string> <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរកសំណួរ"</string> @@ -1103,18 +1103,18 @@ <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string> <string name="preposition_for_time" msgid="5506831244263083793">"នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុងឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string> - <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string> + <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string> <string name="days" msgid="4774547661021344602">"ថ្ងៃ"</string> <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string> <string name="hours" msgid="894424005266852993">"ម៉ោង"</string> - <string name="minute" msgid="9148878657703769868">"នាទី"</string> + <string name="minute" msgid="9148878657703769868">"នាទី"</string> <string name="minutes" msgid="5646001005827034509">"នាទី"</string> - <string name="second" msgid="3184235808021478">"វិនាទី"</string> + <string name="second" msgid="3184235808021478">"វិនាទី"</string> <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string> - <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string> - <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string> - <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string> - <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string> + <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string> + <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string> + <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string> + <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string> <plurals name="duration_seconds"> <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item> <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item> @@ -1130,12 +1130,12 @@ <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហាវីដេអូ"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូនេះមិនត្រឹមត្រូវសម្រាប់ចរន្តចូលឧបករណ៍នេះ។"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិនអាចចាក់វីដេអូនេះ។"</string> - <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string> + <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string> <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="noon" msgid="7245353528818587908">"រសៀល"</string> <string name="Noon" msgid="3342127745230013127">"រសៀល"</string> <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string> - <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string> + <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string> <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="6876518925844129331">"ជ្រើសទាំងអស់"</string> @@ -1152,13 +1152,13 @@ <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្របញ្ចូល"</string> <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាពអត្ថបទ"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string> - <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string> + <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string> <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការ"</string> <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ ដើម្បីមើលព័ត៌មានបន្ថែម ឬបញ្ឈប់កម្មវិធី។"</string> - <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string> - <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string> - <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string> - <string name="no" msgid="5141531044935541497">"បោះបង់"</string> + <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string> + <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string> + <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string> + <string name="no" msgid="5141531044935541497">"បោះបង់"</string> <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string> <string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string> <string name="capital_on" msgid="1544682755514494298">"បើក"</string> @@ -1167,7 +1167,7 @@ <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើសកម្មវិធីដើម"</string> <string name="alwaysUse" msgid="4583018368000610438">"ប្រើតាមលំនាំដើមសម្រាប់សកម្មភាពនេះ។"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាតលំនាំដើមក្នុងការកំណត់ប្រព័ន្ធ > កម្មវិធី > ទាញយក។"</string> - <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string> + <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string> <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើសកម្មវិធីសម្រាប់ឧបករណ៍យូអេសប៊ី"</string> <string name="noApplications" msgid="2991814273936504689">"គ្មានកម្មវិធីអាចអនុវត្តសកម្មភាពនេះ។"</string> <string name="aerr_title" msgid="1905800560317137752"></string> @@ -1178,7 +1178,7 @@ <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string> <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិនឆ្លើយតប។ តើអ្នកចង់បិទវា?"</string> <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិនឆ្លើយតប។ \n\nតើអ្នកចង់បិទវាឬ?"</string> - <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string> + <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string> <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string> <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string> <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រក្លាយជាមិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string> @@ -1260,19 +1260,19 @@ <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះអាចកាត់លុយ"</font>" លើគណនីចល័តរបស់អ្នក។"</string> <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វានឹងគិតថ្លៃសេវាកម្មលើគណនីចល័តរបស់អ្នក។"</font></string> <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string> - <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string> + <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string> <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំជម្រើសរបស់ខ្ញុំ"</string> <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នកអាចប្ដូរវាពេលក្រោយក្នុងការកំណត់ > កម្មវិធី"</string> <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាតជានិច្ច"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំអនុញ្ញាត"</string> <string name="sim_removed_title" msgid="6227712319223226185">"បានដកស៊ីមកាតចេញ"</string> - <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string> + <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string> <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string> <string name="sim_added_title" msgid="3719670512889674693">"បានបន្ថែមស៊ីមកាត"</string> <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីចូលដំណើរការបណ្ដាញចល័ត។"</string> <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើមឡើងវិញ"</string> - <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string> - <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string> + <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string> + <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string> <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string> <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string> <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string> @@ -1350,7 +1350,7 @@ <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យកម្មវិធីដកសេវាកម្មនៃកម្មវិធីផ្ទុកលំនាំដើម ដើម្បីចម្លងមាតិកា។ មិនសម្រាប់ប្រើដោយកម្មវិធីលំនាំដើម។"</string> <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string> <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string> - <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string> + <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string> <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យកម្មវិធីចូលការផ្ទុកមានសុវត្ថិភាព keguard ។"</string> <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យការបង្ហាញ និងលាក់ការការពារ"</string> <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យកម្មវិធីគ្រប់គ្រង keguard ។"</string> @@ -1365,7 +1365,7 @@ <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string> <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string> <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string> - <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string> + <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string> <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string> <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string> <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string> @@ -1374,7 +1374,7 @@ <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="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">"បានស្នើសិទ្ធិ\nសម្រាប់គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string> @@ -1397,12 +1397,12 @@ <string name="no_file_chosen" msgid="6363648562170759465">"គ្មានឯកសារបានជ្រើស"</string> <string name="reset" msgid="2448168080964209908">"កំណត់ឡើងវិញ"</string> <string name="submit" msgid="1602335572089911941">"ដាក់ស្នើ"</string> - <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string> + <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string> <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ ដើម្បីចេញពីរបៀបរថយន្ត។"</string> <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string> <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ ដើម្បីរៀបចំ។"</string> <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string> - <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string> + <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string> <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string> <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការប្រើទិន្នន័យចល័តខ្ពស់"</string> <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែមអំពីការប្រើទិន្នន័យចល័ត។"</string> @@ -1428,7 +1428,7 @@ <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍ផ្ទុកយូអេសប៊ីបច្ចុប្បន្នកំពុងប្រើដោយកុំព្យូទ័រ។"</string> <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្នកាតអេសឌីកំពុងប្រើដោយកុំព្យូទ័រ"</string> <string name="media_unknown_state" msgid="729192782197290385">"មិនស្គាល់ស្ថានភាពមេឌៀខាងក្រៅ។"</string> - <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string> + <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string> <string name="find" msgid="4808270900322985960">"រក"</string> <string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string> <string name="find_next" msgid="5742124618942193978">"រកបន្ទាប់"</string> @@ -1444,7 +1444,7 @@ <string name="sync_undo_deletes" msgid="2941317360600338602">"មិនធ្វើការលុបវិញ"</string> <string name="sync_do_nothing" msgid="3743764740430821845">"មិនធ្វើអ្វីទេឥឡូវ"</string> <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើសគណនី"</string> - <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string> + <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string> <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែមគណនី"</string> <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string> <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string> @@ -1463,15 +1463,15 @@ <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើនឆ្នាំ"</string> <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយឆ្នាំ"</string> <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string> - <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> + <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string> <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string> <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="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិនអាចចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</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> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស ដើម្បីដោះសោ។"</string> @@ -1485,7 +1485,7 @@ <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string> <string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string> <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string> - <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string> + <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string> <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string> <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string> <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បានបិទទិន្នន័យ 2G-3G"</string> @@ -1542,7 +1542,7 @@ <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string> <string name="media_route_status_not_available" msgid="6739899962681886401">"មិនទំនេរ"</string> <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុងប្រើ"</string> - <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string> + <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string> <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string> <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួតគ្នា"</string> <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> @@ -1574,7 +1574,7 @@ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string> - <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> + <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -1683,7 +1683,7 @@ <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string> <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"មិនស្គាល់បញ្ឈរ"</string> <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិនស្គាល់ទេសភាព"</string> - <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string> + <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string> <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុសក្នុងការសរសេរមាតិកា"</string> <string name="reason_unknown" msgid="6048913880184628119">"មិនស្គាល់"</string> <string name="reason_service_unavailable" msgid="7824008732243903268">"មិនបានបើកសេវាកម្មបោះពុម្ព"</string> diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml index 3b275a5..71f55fb 100644 --- a/core/res/res/values-lo-rLA/strings.xml +++ b/core/res/res/values-lo-rLA/strings.xml @@ -1695,7 +1695,7 @@ <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະຈຸບັນ"</string> <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string> <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string> - <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string> + <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string> <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ."</string> <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ສັ້ນເກີນໄປ. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ."</string> <plurals name="restr_pin_countdown"> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 4640f2a..d235eaa 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -187,7 +187,7 @@ <string name="global_action_settings" msgid="1756531602592545966">"การตั้งค่า"</string> <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"โหมดปลอดภัย"</string> - <string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string> + <string name="android_system_label" msgid="6577375335728551336">"ระบบแอนดรอยด์"</string> <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string> <string name="managed_profile_label" msgid="6260850669674791528">"ที่ทำงาน"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"บริการที่ต้องเสียค่าใช้จ่าย"</string> @@ -424,7 +424,7 @@ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"อนุญาตให้แอปพลิเคชันทำให้ส่วนหนึ่งของตัวเองคงอยู่ถาวรในหน่วยความจำ ซึ่งจะจำกัดพื้นที่หน่วยความจำที่ใช้งานได้ของแอปพลิเคชันอื่นๆ และทำให้แท็บเล็ตทำงานช้าลง"</string> <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"อนุญาตให้แอปพลิเคชันทำให้ส่วนหนึ่งของตัวเองคงอยู่ถาวรในหน่วยความจำ ซึ่งจะจำกัดพื้นที่หน่วยความจำที่ใช้งานได้ของแอปพลิเคชันอื่นๆ และทำให้โทรศัพท์ทำงานช้าลง"</string> <string name="permlab_deletePackages" msgid="184385129537705938">"ลบแอปพลิเคชัน"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"อนุญาตให้แอปพลิเคชันลบแพ็กเกจ Android แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ลบแอปพลิเคชันที่สำคัญ"</string> + <string name="permdesc_deletePackages" msgid="7411480275167205081">"อนุญาตให้แอปพลิเคชันลบแพ็กเกจแอนดรอยด์แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ลบแอปพลิเคชันที่สำคัญ"</string> <string name="permlab_clearAppUserData" msgid="274109191845842756">"ลบข้อมูลของแอปพลิเคชันอื่น"</string> <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"อนุญาตให้แอปพลิเคชันล้างข้อมูลผู้ใช้"</string> <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"ลบแคชของแอปพลิเคชันอื่น"</string> @@ -432,7 +432,7 @@ <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="permdesc_installPackages" msgid="5628530972548071284">"อนุญาตให้แอปพลิเคชันติดตั้งแพ็กเกจแอนดรอยด์ใหม่หรือที่อัปเดต แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการเพิ่มแอปพลิเคชันใหม่ๆ ด้วยสิทธิ์ที่สูงนี้ได้ตามต้องการ"</string> <string name="permlab_clearAppCache" msgid="7487279391723526815">"ลบข้อมูลแคชของแอปพลิเคชันทั้งหมด"</string> <string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"อนุญาตให้แอปพลิเคชันสร้างพื้นที่ว่างในที่จัดเก็บข้อมูลของแท็บเล็ต โดยลบไฟล์ในไดเรกทอรีแคชของแอปพลิเคชันอื่นๆ ซึ่งอาจทำให้แอปพลิเคชันอื่นเริ่มทำงานช้ากว่าเดิมเนื่องจากต้องดึงข้อมูลของตนซ้ำ"</string> <string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"อนุญาตให้แอปพลิเคชันสร้างพื้นที่ว่างในที่จัดเก็บข้อมูลของโทรศัพท์ โดยลบไฟล์ในไดเรกทอรีแคชของแอปพลิเคชันอื่นๆ ซึ่งอาจทำให้แอปพลิเคชันอื่นเริ่มทำงานช้ากว่าเดิมเนื่องจากต้องดึงข้อมูลของตนซ้ำ"</string> @@ -1190,7 +1190,7 @@ <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_title" msgid="1584192285441405746">"กำลังอัปเกรดแอนดรอยด์..."</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_complete" msgid="1405954754112999229">"เสร็จสิ้นการบูต"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5fec907..3bf777c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4062,6 +4062,9 @@ The default is one. See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_rowSpan" format="integer" min="1" /> + <!-- The relative proportion of horizontal space that should be allocated to this view + during excess space distribution. --> + <attr name="layout_rowWeight" format="float" /> <!-- The column boundary delimiting the left of the group of cells occupied by this view. --> <attr name="layout_column" /> @@ -4070,6 +4073,9 @@ The default is one. See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_columnSpan" format="integer" min="1" /> + <!-- The relative proportion of vertical space that should be allocated to this view + during excess space distribution. --> + <attr name="layout_columnWeight" format="float" /> <!-- Gravity specifies how a component should be placed in its group of cells. The default is LEFT | BASELINE. See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> @@ -4779,6 +4785,26 @@ <attr name="height" /> </declare-styleable> + <!-- Defines the group used in Vector Drawables. --> + <declare-styleable name="VectorDrawableGroup"> + <!-- The Name of this group --> + <attr name="name" /> + <!-- The amount to rotate the group --> + <attr name="rotation" /> + <!-- The X coordinate of the center of rotation of a group --> + <attr name="pivotX" /> + <!-- The Y coordinate of the center of rotation of a group --> + <attr name="pivotY" /> + <!-- The amount to translate the group on X coordinate --> + <attr name="translateX" format="float"/> + <!-- The amount to translate the group on Y coordinate --> + <attr name="translateY" format="float"/> + <!-- The amount to scale the group on X coordinate --> + <attr name="scaleX" /> + <!-- The amount to scale the group on X coordinate --> + <attr name="scaleY" /> + </declare-styleable> + <!-- Defines the path used in Vector Drawables. --> <declare-styleable name="VectorDrawablePath"> <!-- The Name of this path --> @@ -4787,12 +4813,6 @@ <attr name="strokeWidth" format="float" /> <!-- The opacity of a path stroke --> <attr name="strokeOpacity" format="float" /> - <!-- The amount to rotate the path stroke --> - <attr name="rotation" /> - <!-- The X coordinate of the center of rotation of a path --> - <attr name="pivotX" /> - <!-- The Y coordinate of the center of rotation of a path --> - <attr name="pivotY" /> <!-- The color to stroke the path if not defined implies no stroke--> <attr name="stroke" format="color" /> <!-- The color to fill the path if not defined implies no fill--> @@ -6124,6 +6144,11 @@ <!-- Component name of an activity that allows the user to modify the settings for this trust agent. --> <attr name="settingsActivity" /> + <!-- Title for a preference that allows that user to launch the + activity to modify trust agent settings. --> + <attr name="title" /> + <!-- Summary for the same preference as the title. --> + <attr name="summary" /> </declare-styleable> <!-- =============================== --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2d5477c..5cdbcfa 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2178,6 +2178,10 @@ <public type="attr" name="contentInsetLeft" /> <public type="attr" name="contentInsetRight" /> <public type="attr" name="paddingMode" /> + <public type="attr" name="layout_rowWeight" /> + <public type="attr" name="layout_columnWeight" /> + <public type="attr" name="translateX" /> + <public type="attr" name="translateY" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index a0b3b63..59bd667 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -237,14 +237,6 @@ please see styles_device_defaults.xml. <item name="windowExitAnimation">@anim/fast_fade_out</item> </style> - <!-- Window animations for swipe-dismissable windows. {@hide} --> - <style name="Animation.SwipeDismiss"> - <item name="taskOpenEnterAnimation">@anim/swipe_window_enter</item> - <item name="taskOpenExitAnimation">@anim/swipe_window_exit</item> - <item name="taskCloseEnterAnimation">@anim/swipe_window_enter</item> - <item name="taskCloseExitAnimation">@anim/swipe_window_exit</item> - </style> - <!-- Status Bar Styles --> <style name="TextAppearance.StatusBar"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml index 5bac1f9..0c854d3 100644 --- a/core/res/res/values/styles_micro.xml +++ b/core/res/res/values/styles_micro.xml @@ -14,6 +14,19 @@ limitations under the License. --> <resources> + <style name="Animation.Micro"/> + + <style name="Animation.Micro.Activity" parent="Animation.Holo.Activity"> + <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item> + <item name="activityOpenExitAnimation">@null</item> + <item name="activityCloseEnterAnimation">@null</item> + <item name="activityCloseExitAnimation">@anim/slide_out_micro</item> + <item name="taskOpenEnterAnimation">@anim/slide_in_micro</item> + <item name="taskOpenExitAnimation">@null</item> + <item name="taskCloseEnterAnimation">@null</item> + <item name="taskCloseExitAnimation">@anim/slide_out_micro</item> + </style> + <style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light"> <item name="fullDark">@null</item> <item name="topDark">@null</item> diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml index ebdab5b..7e0467b 100644 --- a/core/res/res/values/themes_micro.xml +++ b/core/res/res/values/themes_micro.xml @@ -19,15 +19,14 @@ <item name="alertDialogStyle">@style/AlertDialog.Micro</item> <item name="dialogTheme">@style/Theme.Micro.Dialog</item> <item name="textViewStyle">@style/Widget.Micro.TextView</item> - <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item> - <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item> + <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item> <item name="windowBackground">@color/black</item> <item name="windowContentOverlay">@null</item> <item name="windowIsFloating">false</item> <item name="windowIsTranslucent">true</item> <item name="windowSwipeToDismiss">true</item> - </style> + </style> <style name="Theme.Micro.Light" parent="Theme.Holo.Light.NoActionBar"> <item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item> @@ -35,7 +34,7 @@ <item name="dialogTheme">@style/Theme.Micro.Dialog</item> <item name="textViewStyle">@style/Widget.Micro.TextView</item> <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item> - <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item> + <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item> <item name="windowBackground">@color/white</item> <item name="windowContentOverlay">@null</item> <item name="windowIsFloating">false</item> diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java new file mode 100644 index 0000000..d4f6b1f --- /dev/null +++ b/core/tests/coretests/src/android/os/FileBridgeTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2014 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.os; + +import android.os.FileBridge.FileBridgeOutputStream; +import android.test.AndroidTestCase; +import android.test.MoreAsserts; + +import libcore.io.Streams; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Random; + +public class FileBridgeTest extends AndroidTestCase { + + private File file; + private FileOutputStream fileOs; + private FileBridge bridge; + private FileBridgeOutputStream client; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + file = getContext().getFileStreamPath("meow.dat"); + file.delete(); + + fileOs = new FileOutputStream(file); + + bridge = new FileBridge(); + bridge.setTargetFile(fileOs.getFD()); + bridge.start(); + client = new FileBridgeOutputStream(bridge.getClientSocket()); + } + + @Override + protected void tearDown() throws Exception { + fileOs.close(); + file.delete(); + } + + private void assertOpen() throws Exception { + assertFalse("expected open", bridge.isClosed()); + } + + private void closeAndAssertClosed() throws Exception { + client.close(); + + // Wait a beat for things to settle down + SystemClock.sleep(200); + assertTrue("expected closed", bridge.isClosed()); + } + + private void assertContents(byte[] expected) throws Exception { + MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file))); + } + + public void testNoWriteNoSync() throws Exception { + assertOpen(); + closeAndAssertClosed(); + } + + public void testNoWriteSync() throws Exception { + assertOpen(); + client.flush(); + closeAndAssertClosed(); + } + + public void testWriteNoSync() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + assertContents("meow".getBytes(StandardCharsets.UTF_8)); + } + + public void testWriteSync() throws Exception { + assertOpen(); + client.write("cake".getBytes(StandardCharsets.UTF_8)); + client.flush(); + closeAndAssertClosed(); + assertContents("cake".getBytes(StandardCharsets.UTF_8)); + } + + public void testWriteSyncWrite() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + client.flush(); + client.write("cake".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + assertContents("meowcake".getBytes(StandardCharsets.UTF_8)); + } + + public void testEmptyWrite() throws Exception { + assertOpen(); + client.write(new byte[0]); + closeAndAssertClosed(); + assertContents(new byte[0]); + } + + public void testWriteAfterClose() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + try { + client.write("cake".getBytes(StandardCharsets.UTF_8)); + fail("wrote after close!"); + } catch (IOException expected) { + } + assertContents("meow".getBytes(StandardCharsets.UTF_8)); + } + + public void testRandomWrite() throws Exception { + final Random r = new Random(); + final ByteArrayOutputStream result = new ByteArrayOutputStream(); + + for (int i = 0; i < 512; i++) { + final byte[] test = new byte[r.nextInt(24169)]; + r.nextBytes(test); + result.write(test); + client.write(test); + client.flush(); + } + + closeAndAssertClosed(); + assertContents(result.toByteArray()); + } + + public void testGiantWrite() throws Exception { + final byte[] test = new byte[263401]; + new Random().nextBytes(test); + + assertOpen(); + client.write(test); + closeAndAssertClosed(); + assertContents(test); + } +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk new file mode 100644 index 0000000..d649154 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2014 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. + +LOCAL_PATH:= $(call my-dir) + + +## The application with a minimal main dex +include $(CLEAR_VARS) + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_SDK_VERSION := current + +LOCAL_PACKAGE_NAME := MultiDexLegacyAndException + +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex + +include $(BUILD_PACKAGE) + +$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses + $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@ + echo "com/android/multidexlegacyandexception/Test.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) + diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml new file mode 100644 index 0000000..7fff711 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.multidexlegacyandexception" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/> + + <application + android:name="com.android.multidexlegacyandexception.TestApplication" + android:label="multidexlegacyandexception" + > + <activity + android:name="com.android.multidexlegacyandexception.MainActivity" + android:label="multidexlegacyandexception" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.multidexlegacyandexception" + android:label="Test for MultiDexLegacyAndException" /> +</manifest> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml new file mode 100644 index 0000000..37eb613 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml @@ -0,0 +1,13 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + tools:context=".MainActivity" > + + <TextView + android:id="@+id/label_nb" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_nb" /> + +</RelativeLayout> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml new file mode 100644 index 0000000..e56e049 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">MultidexLegacyAndException</string> + <string name="action_settings">Settings</string> + <string name="label_nb">Here\'s the count: </string> + +</resources> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java new file mode 100644 index 0000000..d6883ec --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class CaughtOnlyByIntermediateException extends RuntimeException { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java new file mode 100644 index 0000000..4903e01 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class CaughtOnlyException extends RuntimeException { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java new file mode 100644 index 0000000..b08a11a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ClassInSecondaryDex { + private boolean condition; + + public ClassInSecondaryDex(boolean condition) { + this.condition = condition; + } + + public void canThrow1() throws ExceptionInMainDex, ExceptionInMainDex2, + ExceptionInSecondaryDexWithSuperInMain { + if (condition) { + throw new ExceptionInMainDex(); + } + } + + public void canThrow2() throws ExceptionInSecondaryDex, ExceptionInSecondaryDex2, + ExceptionInSecondaryDexWithSuperInMain { + if (condition) { + throw new ExceptionInSecondaryDex(); + } + } + + public static void canThrowAll(Throwable toThrow) throws Throwable { + if (toThrow != null) { + throw toThrow; + } + } + + public int get1() { + try { + canThrow1(); + canThrow2(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) { + return 23; + } + } + + public int get2() { + try { + canThrow2(); + canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex e) { + return 23; + } catch (SuperExceptionInMainDex e) { + return 27; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java new file mode 100644 index 0000000..7fc3d73 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInMainDex extends SuperExceptionInMainDex { +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java new file mode 100644 index 0000000..3fbeac6 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInMainDex2 extends SuperExceptionInMainDex { +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java new file mode 100644 index 0000000..9401c05 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDex extends SuperExceptionInSecondaryDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java new file mode 100644 index 0000000..d1aa103 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDex2 extends SuperExceptionInSecondaryDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java new file mode 100644 index 0000000..9327882 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDexWithSuperInMain extends SuperExceptionInMainDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java new file mode 100644 index 0000000..dfdc4af --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +public class IntermediateClass { + + public static int get1(boolean condition) { + return new ClassInSecondaryDex(condition).get1(); + } + + public static int get2(boolean condition) { + return new ClassInSecondaryDex(condition).get2(); + } + + public static int get3(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInMainDex2 e) { + return 10; + } catch (ExceptionInSecondaryDex2 e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) { + return 23; + } + } + + public static int get4(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInSecondaryDex2 e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (ExceptionInMainDex2 e) { + return 10; + } catch (ExceptionInMainDex e) { + return 10; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex e) { + } catch (SuperExceptionInMainDex e) { + } catch (CaughtOnlyByIntermediateException e) { + return 35; + } + return 39; + } + + + public static int get5(Throwable thrown) { + try { + ClassInSecondaryDex.canThrowAll(thrown); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInMainDex2 e) { + return 12; + } catch (ExceptionInSecondaryDex2 e) { + return 13; + } catch (OutOfMemoryError e) { + return 14; + } catch (CaughtOnlyException e) { + return 17; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) { + return 23; + } catch (Throwable e) { + return 37; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java new file mode 100644 index 0000000..dd2ce7a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +import android.app.Activity; +import android.os.Bundle; + +public class MainActivity extends Activity { + + public MainActivity() { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + } + + public int get1(boolean condition) { + return IntermediateClass.get1(condition); + } + + public int get2(boolean condition) { + return IntermediateClass.get2(condition); + } + public int get3(boolean condition) { + return MiniIntermediateClass.get3(condition); + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java new file mode 100644 index 0000000..5957662 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +public class MiniIntermediateClass { + + public static int get3(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) { + return 23; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java new file mode 100644 index 0000000..c94b30a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class SuperExceptionInMainDex extends Exception { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java new file mode 100644 index 0000000..6366fae --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class SuperExceptionInSecondaryDex extends Exception { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java new file mode 100644 index 0000000..5e931bc --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +import android.test.ActivityInstrumentationTestCase2; + +/** + * Run the tests with: <code>adb shell am instrument -w + com.android.multidexlegacyandexception/android.test.InstrumentationTestRunner +</code> + */ +public class Test extends ActivityInstrumentationTestCase2<MainActivity> { + public Test() { + super(MainActivity.class); + } + + public void testExceptionInMainDex() { + assertEquals(10, TestApplication.get(true)); + } + + public void testExceptionInSecondaryDex() { + assertEquals(10, getActivity().get1(true)); + assertEquals(11, getActivity().get2(true)); + } + + public void testExceptionInIntermediate() { + assertEquals(11, IntermediateClass.get3(true)); + assertEquals(11, MiniIntermediateClass.get3(true)); + assertEquals(11, IntermediateClass.get4(true)); + assertEquals(1, IntermediateClass.get5(null)); + assertEquals(10, IntermediateClass.get5(new ExceptionInMainDex())); + assertEquals(11, IntermediateClass.get5(new ExceptionInSecondaryDex())); + assertEquals(12, IntermediateClass.get5(new ExceptionInMainDex2())); + assertEquals(13, IntermediateClass.get5(new ExceptionInSecondaryDex2())); + assertEquals(14, IntermediateClass.get5(new OutOfMemoryError())); + assertEquals(17, IntermediateClass.get5(new CaughtOnlyException())); + assertEquals(39, IntermediateClass.get5(new ExceptionInSecondaryDexWithSuperInMain())); + assertEquals(23, IntermediateClass.get5(new SuperExceptionInSecondaryDex())); + assertEquals(23, IntermediateClass.get5(new SuperExceptionInMainDex())); + assertEquals(23, IntermediateClass.get5(new CaughtOnlyByIntermediateException())); + assertEquals(37, IntermediateClass.get5(new ArrayIndexOutOfBoundsException())); + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java new file mode 100644 index 0000000..dece9a4 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.multidexlegacyandexception; + +import android.support.multidex.MultiDexApplication; + +public class TestApplication extends MultiDexApplication { + + private static void canThrow1(boolean condition) throws ExceptionInMainDex { + if (condition) { + throw new ExceptionInMainDex(); + } + } + + + public static int get(boolean condition) { + try { + canThrow1(condition); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInMainDex e) { + return 27; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java index 20fe465..7b83999 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java @@ -31,7 +31,7 @@ import java.io.RandomAccessFile; * Empty service for testing legacy multidex. Access more than 64k methods but some are required at * init, some only at verification and others during execution. */ -public abstract class AbstractService extends Service { +public abstract class AbstractService extends Service implements Runnable { private final String TAG = "MultidexLegacyTestService" + getId(); private int instanceFieldNotInited; @@ -47,6 +47,12 @@ public abstract class AbstractService extends Service { @Override public void onCreate() { Log.i(TAG, "onCreate"); + new Thread(this).start(); + + } + + @Override + public void run() { Context applicationContext = getApplicationContext(); File resultFile = new File(applicationContext.getFilesDir(), getId()); try { @@ -84,7 +90,6 @@ public abstract class AbstractService extends Service { } catch (IOException e) { e.printStackTrace(); } - } @Override diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java index 0f343b1..c0c20e2 100644 --- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java @@ -25,8 +25,9 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; -import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController; +import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; +import com.android.internal.inputmethod.InputMethodUtils; import java.util.ArrayList; import java.util.Arrays; @@ -39,6 +40,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe private static final boolean DUMMY_FORCE_DEFAULT = false; private static final int DUMMY_IS_DEFAULT_RES_ID = 0; private static final String SYSTEM_LOCALE = "en_US"; + private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static InputMethodSubtype createDummySubtype(final String locale) { final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder(); @@ -64,142 +66,211 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe si.exported = true; si.nonLocalizedLabel = imeLabel; ri.serviceInfo = si; - final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); - for (String subtypeLocale : subtypeLocales) { - subtypes.add(createDummySubtype(subtypeLocale)); + List<InputMethodSubtype> subtypes = null; + if (subtypeLocales != null) { + subtypes = new ArrayList<InputMethodSubtype>(); + for (String subtypeLocale : subtypeLocales) { + subtypes.add(createDummySubtype(subtypeLocale)); + } } final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME, DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID, DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod); - for (int i = 0; i < subtypes.size(); ++i) { - final String subtypeLocale = subtypeLocales.get(i); - items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale, - SYSTEM_LOCALE)); + if (subtypes == null) { + items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi, + NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE)); + } else { + for (int i = 0; i < subtypes.size(); ++i) { + final String subtypeLocale = subtypeLocales.get(i); + items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale, + SYSTEM_LOCALE)); + } } } - private static List<ImeSubtypeListItem> createTestData() { + private static List<ImeSubtypeListItem> createEnabledImeSubtypes() { final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>(); - addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme", - Arrays.asList("en_US", "es_US", "fr"), + addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"), true /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme", + addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme", Arrays.asList("en_UK", "hi"), false /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme", - Arrays.asList("ja_JP"), + addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null, + false /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"), true /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme", - Arrays.asList("ja_JP"), + addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme", + Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/); + return items; + } + + private static List<ImeSubtypeListItem> createDisabledImeSubtypes() { + final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>(); + addDummyImeSubtypeListItems(items, + "UnknownIme", "UnknownIme", + Arrays.asList("en_US", "hi"), + true /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, + "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme", + Arrays.asList("en_US"), + false /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme", + "UnknownSubtypeUnawareIme", null, false /* supportsSwitchingToNextInputMethod*/); return items; } + private void assertNextInputMethod(final ControllerImpl controller, + final boolean onlyCurrentIme, + final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) { + InputMethodSubtype subtype = null; + if (currentItem.mSubtypeName != null) { + subtype = createDummySubtype(currentItem.mSubtypeName.toString()); + } + final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme, + currentItem.mImi, subtype); + assertEquals(nextItem, nextIme); + } + + private void assertRotationOrder(final ControllerImpl controller, + final boolean onlyCurrentIme, + final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) { + final int N = expectedRotationOrderOfImeSubtypeList.length; + for (int i = 0; i < N; i++) { + final int currentIndex = i; + final int nextIndex = (currentIndex + 1) % N; + final ImeSubtypeListItem currentItem = + expectedRotationOrderOfImeSubtypeList[currentIndex]; + final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex]; + assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem); + } + } + + private void onUserAction(final ControllerImpl controller, + final ImeSubtypeListItem subtypeListItem) { + InputMethodSubtype subtype = null; + if (subtypeListItem.mSubtypeName != null) { + subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString()); + } + controller.onUserActionLocked(subtypeListItem.mImi, subtype); + } + @SmallTest - public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception { - final List<ImeSubtypeListItem> imList = createTestData(); - - final boolean ONLY_CURRENT_IME = false; - ImeSubtypeListItem currentIme; - ImeSubtypeListItem nextIme; - - // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US" - currentIme = imList.get(0); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(1), nextIme); - // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr" - currentIme = imList.get(1); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(2), nextIme); - // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP" - currentIme = imList.get(2); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(5), nextIme); - // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US" - currentIme = imList.get(5); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(0), nextIme); - - // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi" - currentIme = imList.get(3); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(4), nextIme); - // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP" - currentIme = imList.get(4); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(6), nextIme); - // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK" - currentIme = imList.get(6); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(3), nextIme); + public void testControllerImpl() throws Exception { + final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes(); + final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0); + final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1); + final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2); + final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3); + + final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); + final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); + final ImeSubtypeListItem latinIme_fr = enabledItems.get(1); + final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2); + final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3); + final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4); + final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); + final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); + + final ControllerImpl controller = new ControllerImpl(enabledItems); + + // switching-aware loop + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr, japaneseIme_ja_JP); + + // switching-unaware loop + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + + // test onlyCurrentIme == true + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + assertRotationOrder(controller, true /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + subtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + japaneseIme_ja_JP, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + switchUnawareJapaneseIme_ja_JP, null); + + // Make sure that disabled IMEs are not accepted. + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledIme_en_US, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledIme_hi, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledSwitchingUnawareIme, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledSubtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledIme_en_US, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledIme_hi, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledSwitchingUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledSubtypeUnawareIme, null); } + // This test is disabled until DynamicRotationList is enabled. @SmallTest - public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception { - final List<ImeSubtypeListItem> imList = createTestData(); - - final boolean ONLY_CURRENT_IME = true; - ImeSubtypeListItem currentIme; - ImeSubtypeListItem nextIme; - - // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US" - currentIme = imList.get(0); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(1), nextIme); - // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr" - currentIme = imList.get(1); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(2), nextIme); - // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US" - currentIme = imList.get(2); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(0), nextIme); - - // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi" - currentIme = imList.get(3); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(4), nextIme); - // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK" - currentIme = imList.get(4); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(3), nextIme); - - // "switchAwareJapaneseIme/ja_JP" -> null - currentIme = imList.get(5); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertNull(nextIme); - - // "nonSwitchAwareJapaneseIme/ja_JP" -> null - currentIme = imList.get(6); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertNull(nextIme); + public void DISABLED_testControllerImplWithUserAction() throws Exception { + final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); + final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); + final ImeSubtypeListItem latinIme_fr = enabledItems.get(1); + final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2); + final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3); + final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4); + final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); + final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); + + final ControllerImpl controller = new ControllerImpl(enabledItems); + + // === switching-aware loop === + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr, japaneseIme_ja_JP); + // Then notify that a user did something for latinIme_fr. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US, japaneseIme_ja_JP); + // Then notify that a user did something for latinIme_fr again. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US, japaneseIme_ja_JP); + // Then notify that a user did something for japaneseIme_ja_JP. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + japaneseIme_ja_JP, latinIme_fr, latinIme_en_US); + // Check onlyCurrentIme == true. + assertNextInputMethod(controller, true /* onlyCurrentIme */, + japaneseIme_ja_JP, null); + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US); + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + + // === switching-unaware loop === + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // User action should be ignored for switching unaware IMEs. + onUserAction(controller, switchingUnawarelatinIme_hi); + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // User action should be ignored for switching unaware IMEs. + onUserAction(controller, switchUnawareJapaneseIme_ja_JP); + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // Check onlyCurrentIme == true. + assertRotationOrder(controller, true /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + subtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + switchUnawareJapaneseIme_ja_JP, null); } - } +} |