diff options
Diffstat (limited to 'core')
98 files changed, 3995 insertions, 458 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 28fc21a..8bb305d 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -25,6 +25,7 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; /** * An accessibility service runs in the background and receives callbacks by the system @@ -51,7 +52,11 @@ import android.view.accessibility.AccessibilityEvent; * enabling or disabling it in the device settings. After the system binds to a service it * calls {@link AccessibilityService#onServiceConnected()}. This method can be * overriden by clients that want to perform post binding setup. + * </p> * <p> + * An accessibility service can be configured to receive specific types of accessibility events, + * listen only to specific packages, get events from each type only once in a given time frame, + * retrieve window content, specify a settings activity, etc. * </p> * There are two approaches for configuring an accessibility service: * <ul> @@ -59,20 +64,23 @@ import android.view.accessibility.AccessibilityEvent; * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring * the service. A service declaration with a meta-data tag is presented below: * <p> - * <code> - * <service android:name=".MyAccessibilityService"><br> - * <intent-filter><br> - * <action android:name="android.accessibilityservice.AccessibilityService" /><br> - * </intent-filter><br> - * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br> - * </service><br> - * </code> + * <code> + * <service android:name=".MyAccessibilityService"><br> + * <intent-filter><br> + * <action android:name="android.accessibilityservice.AccessibilityService" /><br> + * </intent-filter><br> + * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br> + * </service><br> + * </code> * </p> * <p> * <strong> * This approach enables setting all accessibility service properties. * </strong> * </p> + * <p> + * For more details refer to {@link #SERVICE_META_DATA}. + * </p> * </li> * <li> * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note @@ -88,6 +96,9 @@ import android.view.accessibility.AccessibilityEvent; * {@link AccessibilityServiceInfo#packageNames} * </strong> * </p> + * <p> + * For more details refer to {@link AccessibilityServiceInfo}. + * </p> * </li> * </ul> * <p> @@ -151,16 +162,49 @@ public abstract class AccessibilityService extends Service { * <code> * <?xml version="1.0" encoding="utf-8"?><br> * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br> - * android:eventTypes="typeViewClicked|typeViewFocused"<br> + * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br> * android:packageNames="foo.bar, foo.baz"<br> - * android:feedbackType="feedbackSpoken"<br> + * android:accessibilityFeedbackType="feedbackSpoken"<br> * android:notificationTimeout="100"<br> - * android:flags="flagDefault"<br> + * android:accessibilityFlags="flagDefault"<br> * android:settingsActivity="foo.bar.TestBackActivity"<br> * . . .<br> * /> * </code> * </p> + * <p> + * <strong>Note:</strong> A service can retrieve only the content of the active window. + * An active window is the source of the most recent event of type + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, + * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, + * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}. + * Therefore the service should: + * <ul> + * <li> + * Register for all event types with no notification timeout and keep track + * for the active window by calling + * {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received + * event and compare this with the + * {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling + * retrieval methods on the latter. + * </li> + * <li> + * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail + * since the active window has changed and the service did not get the + * accessibility event. Note that it is possible to have a retrieval method + * failing event adopting the strategy specified in the previous bullet + * because the accessibility event dispatch is asynchronous and crosses + * process boundaries. + * </li> + * <ul> + * </p> */ public static final String SERVICE_META_DATA = "android.accessibilityservice"; @@ -224,7 +268,7 @@ public abstract class AccessibilityService extends Service { /** * Implement to return the implementation of the internal accessibility - * service interface. Subclasses should not override. + * service interface. */ @Override public final IBinder onBind(Intent intent) { diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 7157def..19f0bf0 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -17,14 +17,72 @@ package android.accessibilityservice; import android.accessibilityservice.AccessibilityServiceInfo; +import android.view.accessibility.AccessibilityNodeInfo; /** - * Interface AccessibilityManagerService#Service implements, and passes to an - * AccessibilityService so it can dynamically configure how the system handles it. + * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService. * * @hide */ -oneway interface IAccessibilityServiceConnection { +interface IAccessibilityServiceConnection { void setServiceInfo(in AccessibilityServiceInfo info); + + /** + * Finds an {@link AccessibilityNodeInfo} by accessibility id. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * + * @param accessibilityWindowId A unique window id. + * @param accessibilityViewId A unique View accessibility id. + * @return The node info. + */ + AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + int accessibilityViewId); + + /** + * Finds {@link AccessibilityNodeInfo}s by View text. The match is case + * insensitive containment. + * <p> + * <strong> + * It is a client responsibility to recycle the received infos by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * + * @param text The searched text. + * @return A list of node info. + */ + List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text); + + /** + * Finds an {@link AccessibilityNodeInfo} by View id. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * + * @param id The id of the node. + * @return The node info. + */ + AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId); + + /** + * Performs an accessibility action on an {@link AccessibilityNodeInfo}. + * + * @param accessibilityWindowId The id of the window. + * @param accessibilityViewId The of a view in the . + * @return Whether the action was performed. + */ + boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId, + int action); } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 26707c9..a8c31f9 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -873,10 +873,10 @@ public final class BluetoothAdapter { /** * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. - * <p>The link key will be unauthenticated i.e the communication is + * <p>The link key is not required to be authenticated, i.e the communication may be * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, - * the link key will be encrypted, as encryption is mandartory. - * For legacy devices (pre Bluetooth 2.1 devices) the link key will not + * the link will be encrypted, as encryption is mandartory. + * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an * encrypted and authenticated communication channel is desired. * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming @@ -902,6 +902,44 @@ public final class BluetoothAdapter { return createNewRfcommSocketAndRecord(name, uuid, false, false); } + /** + * Create a listening, encrypted, + * RFCOMM Bluetooth socket with Service Record. + * <p>The link will be encrypted, but the link key is not required to be authenticated + * i.e the communication is vulnerable to Man In the Middle attacks. Use + * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. + * <p> Use this socket if authentication of link key is not possible. + * For example, for Bluetooth 2.1 devices, if any of the devices does not have + * an input and output capability or just has the ability to display a numeric key, + * a secure socket connection is not possible and this socket can be used. + * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. + * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. + * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + * <p>The system will assign an unused RFCOMM channel to listen on. + * <p>The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord( + String name, UUID uuid) throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, false, true); + } + private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { RfcommChannelPicker picker = new RfcommChannelPicker(uuid); @@ -973,6 +1011,28 @@ public final class BluetoothAdapter { return socket; } + /** + * Construct an encrypted, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) + throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, false, true, port); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + /** * Construct a SCO server socket. * Call #accept to retrieve connections to this socket. diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index d5c4ace..dea5133 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -16,22 +16,22 @@ package android.hardware; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.StringTokenizer; -import java.io.IOException; - -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.StringTokenizer; /** * The Camera class is used to set image capture settings, start/stop preview, @@ -834,8 +834,6 @@ public class Camera { * @param raw the callback for raw (uncompressed) image data, or null * @param postview callback with postview image data, may be null * @param jpeg the callback for JPEG image data, or null - * - * @see #addRawImageCallbackBuffer(byte[]) */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) { @@ -1084,10 +1082,14 @@ public class Camera { }; /** - * Area class for focus. - * - * @see #setFocusAreas(List) - * @see #getFocusAreas() + * Area class for focus and metering. + * + * @see Parameters#setFocusAreas(List) + * @see Parameters#getFocusAreas() + * @see Parameters#getMaxNumFocusAreas() + * @see Parameters#setMeteringAreas(List) + * @see Parameters#getMeteringAreas() + * @see Parameters#getMaxNumMeteringAreas() */ public static class Area { /** @@ -1121,12 +1123,22 @@ public class Camera { return weight == a.weight; } - /** rectangle of the area */ + /** + * Rectangle of the area. + * + * @see Parameters#getFocusAreas() + * @see Parameters#getMeteringAreas() + */ public Rect rect; - /** weight of the area */ + /** + * Weight of the area. + * + * @see Parameters#getFocusAreas() + * @see Parameters#getMeteringAreas() + */ public int weight; - }; + } /** * Camera service settings. @@ -2775,7 +2787,7 @@ public class Camera { * The direction is not affected by the rotation or mirroring of * {@link #setDisplayOrientation(int)}. Coordinates of the rectangle * range from -1000 to 1000. (-1000, -1000) is the upper left point. - * (1000, 1000) is the lower right point. The length and width of focus + * (1000, 1000) is the lower right point. The width and height of focus * areas cannot be 0 or negative. * * The weight must range from 1 to 1000. The weight should be @@ -2842,7 +2854,7 @@ public class Camera { * sensor sees. The direction is not affected by the rotation or * mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the * rectangle range from -1000 to 1000. (-1000, -1000) is the upper left - * point. (1000, 1000) is the lower right point. The length and width of + * point. (1000, 1000) is the lower right point. The width and height of * metering areas cannot be 0 or negative. * * The weight must range from 1 to 1000, and represents a weight for @@ -3033,7 +3045,7 @@ public class Camera { if (result.size() == 0) return null; if (result.size() == 1) { - Area area = (Area) result.get(0); + Area area = result.get(0); Rect rect = area.rect; if (rect.left == 0 && rect.top == 0 && rect.right == 0 && rect.bottom == 0 && area.weight == 0) { diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 3bf64b2..8e50cd5 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -28,6 +28,7 @@ import java.security.cert.X509Certificate; import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; @@ -86,6 +87,8 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private SSLSocketFactory mInsecureFactory = null; private SSLSocketFactory mSecureFactory = null; + private TrustManager[] mTrustManagers = null; + private KeyManager[] mKeyManagers = null; private final int mHandshakeTimeoutMillis; private final SSLClientSessionCache mSessionCache; @@ -197,10 +200,11 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } - private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { + private SSLSocketFactory makeSocketFactory( + KeyManager[] keyManagers, TrustManager[] trustManagers) { try { OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, trustManagers, null); + sslContext.engineInit(keyManagers, trustManagers, null); sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache); return sslContext.engineGetSocketFactory(); } catch (KeyManagementException e) { @@ -223,18 +227,44 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } else { Log.w(TAG, "Bypassing SSL security checks at caller's request"); } - mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER); + mInsecureFactory = makeSocketFactory(mKeyManagers, INSECURE_TRUST_MANAGER); } return mInsecureFactory; } else { if (mSecureFactory == null) { - mSecureFactory = makeSocketFactory(null); + mSecureFactory = makeSocketFactory(mKeyManagers, mTrustManagers); } return mSecureFactory; } } /** + * Sets the {@link TrustManager}s to be used for connections made by this factory. + * @hide + */ + public void setTrustManagers(TrustManager[] trustManager) { + mTrustManagers = trustManager; + + // Clear out all cached secure factories since configurations have changed. + mSecureFactory = null; + // Note - insecure factories only ever use the INSECURE_TRUST_MANAGER so they need not + // be cleared out here. + } + + /** + * Sets the {@link KeyManager}s to be used for connections made by this factory. + * @hide + */ + public void setKeyManagers(KeyManager[] keyManagers) { + mKeyManagers = keyManagers; + + // Clear out any existing cached factories since configurations have changed. + mSecureFactory = null; + mInsecureFactory = null; + } + + + /** * {@inheritDoc} * * <p>This method verifies the peer's certificate hostname after connecting diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java index 69329df..3adcdc3 100644 --- a/core/java/android/nfc/ErrorCodes.java +++ b/core/java/android/nfc/ErrorCodes.java @@ -57,6 +57,7 @@ public class ErrorCodes { case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED"; case ERROR_SE_CONNECTED: return "SE_CONNECTED"; case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED"; + case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED"; default: return "UNKNOWN ERROR"; } } @@ -105,4 +106,6 @@ public class ErrorCodes { public static final int ERROR_NO_SE_CONNECTED = -20; -}
\ No newline at end of file + public static final int ERROR_NOT_SUPPORTED = -21; + +} diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java index 7ec807a..6557ee0 100644 --- a/core/java/android/nfc/tech/BasicTagTechnology.java +++ b/core/java/android/nfc/tech/BasicTagTechnology.java @@ -77,6 +77,10 @@ import java.io.IOException; // Store this in the tag object mTag.setConnectedTechnology(mSelectedTechnology); mIsConnected = true; + } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) { + throw new UnsupportedOperationException("Connecting to " + + "this technology is not supported by the NFC " + + "adapter."); } else { throw new IOException(); } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index eb0cf37..215e836 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -19,20 +19,21 @@ package android.os; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.CheckedInputStream; /** * Tools for managing files. Not for public consumption. * @hide */ -public class FileUtils -{ +public class FileUtils { public static final int S_IRWXU = 00700; public static final int S_IRUSR = 00400; public static final int S_IWUSR = 00200; @@ -95,7 +96,7 @@ public class FileUtils /** returns the FAT file system volume ID for the volume mounted * at the given mount point, or -1 for failure - * @param mount point for FAT volume + * @param mountPoint point for FAT volume * @return volume ID or -1 */ public static native int getFatVolumeId(String mountPoint); @@ -243,4 +244,32 @@ public class FileUtils out.close(); } } + + /** + * Computes the checksum of a file using the CRC32 checksum routine. + * The value of the checksum is returned. + * + * @param file the file to checksum, must not be null + * @return the checksum value or an exception is thrown. + */ + public static long checksumCrc32(File file) throws FileNotFoundException, IOException { + CRC32 checkSummer = new CRC32(); + CheckedInputStream cis = null; + + try { + cis = new CheckedInputStream( new FileInputStream(file), checkSummer); + byte[] buf = new byte[128]; + while(cis.read(buf) >= 0) { + // Just read for checksum to get calculated. + } + return checkSummer.getValue(); + } finally { + if (cis != null) { + try { + cis.close(); + } catch (IOException e) { + } + } + } + } } diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 0ef38bf..2df2688 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -16,6 +16,7 @@ package android.provider; + import android.accounts.Account; import android.app.AlarmManager; import android.app.PendingIntent; @@ -32,7 +33,6 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; -import android.pim.ICalendar; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; @@ -44,24 +44,30 @@ import android.util.Log; * @hide */ public final class Calendar { - - public static final String TAG = "Calendar"; + private static final String TAG = "Calendar"; /** - * Broadcast Action: An event reminder. + * Broadcast Action: This is the intent that gets fired when an alarm + * notification needs to be posted for a reminder. */ public static final String EVENT_REMINDER_ACTION = "android.intent.action.EVENT_REMINDER"; /** - * These are the symbolic names for the keys used in the extra data - * passed in the intent for event reminders. + * Intent Extras key: The start time of an event or an instance of a + * recurring event. (milliseconds since epoch) */ public static final String EVENT_BEGIN_TIME = "beginTime"; + + /** + * Intent Extras key: The end time of an event or an instance of a recurring + * event. (milliseconds since epoch) + */ public static final String EVENT_END_TIME = "endTime"; /** - * This must not be changed or horrible, unspeakable things could happen. - * For instance, the Calendar app might break. Also, the db might not work. + * This authority is used for writing to or querying from the calendar + * provider. Note: This is set at first run and cannot be changed without + * breaking apps that access the provider. */ public static final String AUTHORITY = "com.android.calendar"; @@ -73,20 +79,37 @@ public final class Calendar { /** * An optional insert, update or delete URI parameter that allows the caller - * to specify that it is a sync adapter. The default value is false. If true - * the dirty flag is not automatically set and the "syncToNetwork" parameter - * is set to false when calling - * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}. + * to specify that it is a sync adapter. The default value is false. If set + * to true, the modified row is not marked as "dirty" (needs to be synced) + * and when the provider calls + * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)} + * , the third parameter "syncToNetwork" is set to false. Furthermore, if + * set to true, the caller must also include + * {@link SyncColumns#ACCOUNT_NAME} and {@link SyncColumns#ACCOUNT_TYPE} as + * query parameters. + * + * @See Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String) */ public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; + /** + * A special account type for calendars not associated with any account. + * Normally calendars that do not match an account on the device will be + * removed. Setting the account_type on a calendar to this will prevent it + * from being wiped if it does not match an existing account. + * + * @see SyncColumns#ACCOUNT_TYPE + */ + public static final String ACCOUNT_TYPE_LOCAL = "LOCAL"; /** - * Generic columns for use by sync adapters. The specific functions of - * these columns are private to the sync adapter. Other clients of the API - * should not attempt to either read or write this column. + * Generic columns for use by sync adapters. The specific functions of these + * columns are private to the sync adapter. Other clients of the API should + * not attempt to either read or write this column. These columns are + * editable as part of the Calendars Uri, but can only be read if accessed + * through any other Uri. */ - protected interface BaseSyncColumns { + protected interface CalendarSyncColumns { /** Generic column for use by sync adapters. */ public static final String CAL_SYNC1 = "cal_sync1"; @@ -103,29 +126,41 @@ public final class Calendar { } /** - * Columns for Sync information used by Calendars and Events tables. + * Columns for Sync information used by Calendars and Events tables. These + * have specific uses which are expected to be consistent by the app and + * sync adapter. + * + * @hide */ - public interface SyncColumns extends BaseSyncColumns { + public interface SyncColumns extends CalendarSyncColumns { /** - * The account that was used to sync the entry to the device. + * The account that was used to sync the entry to the device. If the + * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and + * type must match an account on the device or the calendar will be + * deleted. * <P>Type: TEXT</P> */ public static final String ACCOUNT_NAME = "account_name"; /** - * The type of the account that was used to sync the entry to the device. + * The type of the account that was used to sync the entry to the + * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event + * form being deleted if there are no matching accounts on the device. * <P>Type: TEXT</P> */ public static final String ACCOUNT_TYPE = "account_type"; /** - * The unique ID for a row assigned by the sync source. NULL if the row has never been synced. + * The unique ID for a row assigned by the sync source. NULL if the row + * has never been synced. This is used as a reference id for exceptions + * along with {@link BaseColumns#_ID}. * <P>Type: TEXT</P> */ public static final String _SYNC_ID = "_sync_id"; /** - * The last time, from the sync source's point of view, that this row has been synchronized. + * The last time, from the sync source's point of view, that this row + * has been synchronized. * <P>Type: INTEGER (long)</P> */ public static final String _SYNC_TIME = "_sync_time"; @@ -151,9 +186,9 @@ public final class Calendar { } /** - * Columns from the Calendars table that other tables join into themselves. + * Columns specific to the Calendars Uri that other Uris can query. */ - public interface CalendarsColumns { + private interface CalendarsColumns { /** * The color of the calendar * <P>Type: INTEGER (color value)</P> @@ -172,10 +207,17 @@ public final class Calendar { public static final int FREEBUSY_ACCESS = 100; /** Can read all event details */ public static final int READ_ACCESS = 200; + /** Can reply yes/no/maybe to an event */ public static final int RESPOND_ACCESS = 300; + /** not used */ public static final int OVERRIDE_ACCESS = 400; - /** Full access to modify the calendar, but not the access control settings */ + /** Full access to modify the calendar, but not the access control + * settings + */ public static final int CONTRIBUTOR_ACCESS = 500; + /** Full access to modify the calendar, but not the access control + * settings + */ public static final int EDITOR_ACCESS = 600; /** Full access to the calendar */ public static final int OWNER_ACCESS = 700; @@ -184,31 +226,35 @@ public final class Calendar { /** * Is the calendar selected to be displayed? + * 0 - do not show events associated with this calendar. + * 1 - show events associated with this calendar * <P>Type: INTEGER (boolean)</P> */ public static final String VISIBLE = "visible"; /** - * The timezone the calendar's events occurs in + * The time zone the calendar is associated with. * <P>Type: TEXT</P> */ public static final String CALENDAR_TIMEZONE = "calendar_timezone"; /** - * If this calendar is in the list of calendars that are selected for - * syncing then "sync_events" is 1, otherwise 0. + * Is this calendar synced and are its events stored on the device? + * 0 - Do not sync this calendar or store events for this calendar. + * 1 - Sync down events for this calendar. * <p>Type: INTEGER (boolean)</p> */ public static final String SYNC_EVENTS = "sync_events"; /** - * Sync state data. + * Sync state data. Usable by the sync adapter. * <p>Type: String (blob)</p> */ public static final String SYNC_STATE = "sync_state"; /** - * Whether the row has been deleted. A deleted row should be ignored. + * Whether the row has been deleted but not synced to the server. A + * deleted row should be ignored. * <P>Type: INTEGER (boolean)</P> */ public static final String DELETED = "deleted"; @@ -216,35 +262,32 @@ public final class Calendar { /** * Class that represents a Calendar Entity. There is one entry per calendar. + * This is a helper class to make batch operations easier. */ public static class CalendarsEntity implements BaseColumns, SyncColumns, CalendarsColumns { + /** + * The default Uri used when creating a new calendar EntityIterator. + */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendar_entities"); - public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) { - return new EntityIteratorImpl(cursor, resolver); - } - - public static EntityIterator newEntityIterator(Cursor cursor, - ContentProviderClient provider) { - return new EntityIteratorImpl(cursor, provider); + /** + * Creates an entity iterator for the given cursor. It assumes the + * cursor contains a calendars query. + * + * @param cursor query on {@link #CONTENT_URI} + * @return an EntityIterator of calendars + */ + public static EntityIterator newEntityIterator(Cursor cursor) { + return new EntityIteratorImpl(cursor); } private static class EntityIteratorImpl extends CursorEntityIterator { - private final ContentResolver mResolver; - private final ContentProviderClient mProvider; - public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) { + public EntityIteratorImpl(Cursor cursor) { super(cursor); - mResolver = resolver; - mProvider = null; - } - - public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) { - super(cursor); - mResolver = null; - mProvider = provider; } @Override @@ -305,27 +348,40 @@ public final class Calendar { } /** - * Contains a list of available calendars. + * Fields and helpers for interacting with Calendars. */ - public static class Calendars implements BaseColumns, SyncColumns, - CalendarsColumns - { + public static class Calendars implements BaseColumns, SyncColumns, CalendarsColumns { private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?" + " AND " + Calendars.ACCOUNT_TYPE + "=?"; - public static final Cursor query(ContentResolver cr, String[] projection, - String where, String orderBy) - { - return cr.query(CONTENT_URI, projection, where, - null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); + /** + * Helper function for generating a calendars query. This is blocking + * and should not be used on the UI thread. See + * {@link ContentResolver#query(Uri, String[], String, String[], String)} + * for more details about using the parameters. + * + * @param cr The ContentResolver to query with + * @param projection A list of columns to return + * @param selection A formatted selection string + * @param selectionArgs arguments to the selection string + * @param orderBy How to order the returned rows + * @return + */ + public static final Cursor query(ContentResolver cr, String[] projection, String selection, + String[] selectionArgs, String orderBy) { + return cr.query(CONTENT_URI, projection, selection, selectionArgs, + orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** - * Convenience method perform a delete on the Calendar provider + * Convenience method perform a delete on the Calendar provider. This is + * a blocking call and should not be used on the UI thread. * * @param cr the ContentResolver - * @param selection the rows to delete + * @param selection A filter to apply to rows before deleting, formatted + * as an SQL WHERE clause (excluding the WHERE itself). + * @param selectionArgs Fill in the '?'s in the selection * @return the count of rows that were deleted */ public static int delete(ContentResolver cr, String selection, String[] selectionArgs) @@ -335,10 +391,12 @@ public final class Calendar { /** * Convenience method to delete all calendars that match the account. + * This is a blocking call and should not be used on the UI thread. * * @param cr the ContentResolver - * @param account the account whose rows should be deleted - * @return the count of rows that were deleted + * @param account the account whose calendars and events should be + * deleted + * @return the count of calendar rows that were deleted */ public static int deleteCalendarsForAccount(ContentResolver cr, Account account) { // delete all calendars that match this account @@ -348,8 +406,9 @@ public final class Calendar { } /** - * The content:// style URL for this table + * The content:// style URL for accessing Calendars */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars"); /** @@ -358,43 +417,43 @@ public final class Calendar { public static final String DEFAULT_SORT_ORDER = "displayName"; /** - * The URL to the calendar + * The URL to the calendar. Column name. * <P>Type: TEXT (URL)</P> */ public static final String URL = "url"; /** - * The URL for the calendar itself + * The URL for the calendar itself. Column name. * <P>Type: TEXT (URL)</P> */ public static final String SELF_URL = "selfUrl"; /** - * The URL for the calendar to be edited + * The URL for the calendar to be edited. Column name. * <P>Type: TEXT (URL)</P> */ public static final String EDIT_URL = "editUrl"; /** - * The URL for the calendar events + * The URL for the calendar events. Column name. * <P>Type: TEXT (URL)</P> */ public static final String EVENTS_URL = "eventsUrl"; /** - * The name of the calendar + * The name of the calendar. Column name. * <P>Type: TEXT</P> */ public static final String NAME = "name"; /** - * The display name of the calendar + * The display name of the calendar. Column name. * <P>Type: TEXT</P> */ public static final String DISPLAY_NAME = "displayName"; /** - * The location the of the events in the calendar + * The default location for the calendar. Column name. * <P>Type: TEXT</P> */ public static final String CALENDAR_LOCATION = "calendar_location"; @@ -402,41 +461,45 @@ public final class Calendar { /** * The owner account for this calendar, based on the calendar feed. * This will be different from the _SYNC_ACCOUNT for delegated calendars. + * Column name. * <P>Type: String</P> */ public static final String OWNER_ACCOUNT = "ownerAccount"; /** * Can the organizer respond to the event? If no, the status of the - * organizer should not be shown by the UI. Defaults to 1 + * organizer should not be shown by the UI. Defaults to 1. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond"; /** - * Can the organizer modify the time zone of the event? + * Can the organizer modify the time zone of the event? Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone"; /** - * The maximum number of reminders allowed for an event. + * The maximum number of reminders allowed for an event. Column name. * <P>Type: INTEGER</P> */ public static final String MAX_REMINDERS = "maxReminders"; /** - * The maximum number of reminders allowed for an event. - * <P> - * Type: INTEGER - * </P> + * A comma separated list of reminder methods supported for this + * calendar in the format "#,#,#". Valid types are + * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT}, + * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column + * name. + * <P>Type: TEXT</P> */ public static final String ALLOWED_REMINDERS = "allowedReminders"; /** * These fields are only writable by a sync adapter. To modify them the - * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and - * _SYNC_ACCOUNT_TYPE in the query parameters. + * caller must include {@link #CALLER_IS_SYNCADAPTER}, + * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the query + * parameters. */ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, @@ -457,7 +520,8 @@ public final class Calendar { CAL_SYNC2, CAL_SYNC3, CAL_SYNC4, - CAL_SYNC5, CAL_SYNC6, + CAL_SYNC5, + CAL_SYNC6, SYNC_STATE, }; } @@ -465,29 +529,29 @@ public final class Calendar { /** * Columns from the Attendees table that other tables join into themselves. */ - public interface AttendeesColumns { + private interface AttendeesColumns { /** - * The id of the event. + * The id of the event. Column name. * <P>Type: INTEGER</P> */ public static final String EVENT_ID = "event_id"; /** - * The name of the attendee. + * The name of the attendee. Column name. * <P>Type: STRING</P> */ public static final String ATTENDEE_NAME = "attendeeName"; /** - * The email address of the attendee. + * The email address of the attendee. Column name. * <P>Type: STRING</P> */ public static final String ATTENDEE_EMAIL = "attendeeEmail"; /** - * The relationship of the attendee to the user. - * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}. + * The relationship of the attendee to the user. Column name. + * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P> */ public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship"; @@ -498,8 +562,8 @@ public final class Calendar { public static final int RELATIONSHIP_SPEAKER = 4; /** - * The type of attendee. - * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL}) + * The type of attendee. Column name. + * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})</P> */ public static final String ATTENDEE_TYPE = "attendeeType"; @@ -508,8 +572,8 @@ public final class Calendar { public static final int TYPE_OPTIONAL = 2; /** - * The attendance status of the attendee. - * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...}. + * The attendance status of the attendee. Column name. + * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P> */ public static final String ATTENDEE_STATUS = "attendeeStatus"; @@ -520,59 +584,84 @@ public final class Calendar { public static final int ATTENDEE_STATUS_TENTATIVE = 4; } + /** + * Fields and helpers for interacting with Attendees. + */ public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns { + + /** + * The content:// style URL for accessing Attendees data + */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees"); + /** + * the projection used by the attendees query + */ + public static final String[] PROJECTION = new String[] { + _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,}; + private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; - // TODO: fill out this class when we actually start utilizing attendees - // in the calendar application. + /** + * Queries all attendees associated with the given event. This is a + * blocking call and should not be done on the UI thread. + * + * @param cr The content resolver to use for the query + * @param eventId The id of the event to retrieve attendees for + * @return A Cursor containing all attendees for the event + */ + public static final Cursor query(ContentResolver cr, long eventId) { + String[] attArgs = {Long.toString(eventId)}; + return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */, + null /* sort order */); + } } /** * Columns from the Events table that other tables join into themselves. */ - public interface EventsColumns { + private interface EventsColumns { /** - * For use by sync adapter at its discretion; not modified by CalendarProvider - * Note that this column was formerly named _SYNC_LOCAL_ID. We are using it to avoid a - * schema change. - * TODO Replace this with something more general in the future. + * For use by sync adapter at its discretion. Column name. + * TODO change to sync_data2 * <P>Type: INTEGER (long)</P> */ public static final String _SYNC_DATA = "_sync_local_id"; /** - * The calendar the event belongs to - * <P>Type: INTEGER (foreign key to the Calendars table)</P> + * The {@link Calendars#_ID} of the calendar the event belongs to. + * Column name. + * <P>Type: INTEGER</P> */ public static final String CALENDAR_ID = "calendar_id"; /** - * The URI for an HTML version of this event. + * The URI for an HTML version of this event. Column name. + * TODO change to sync_data3 * <P>Type: TEXT</P> */ public static final String HTML_URI = "htmlUri"; /** - * The title of the event + * The title of the event. Column name. * <P>Type: TEXT</P> */ public static final String TITLE = "title"; /** - * The description of the event + * The description of the event. Column name. * <P>Type: TEXT</P> */ public static final String DESCRIPTION = "description"; /** - * Where the event takes place. + * Where the event takes place. Column name. * <P>Type: TEXT</P> */ public static final String EVENT_LOCATION = "eventLocation"; /** - * The event status - * <P>Type: INTEGER (int)</P> + * The event status. Column name. + * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P> */ public static final String STATUS = "eventStatus"; @@ -584,64 +673,65 @@ public final class Calendar { * This is a copy of the attendee status for the owner of this event. * This field is copied here so that we can efficiently filter out * events that are declined without having to look in the Attendees - * table. + * table. Column name. * * <P>Type: INTEGER (int)</P> */ public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus"; /** - * This column is available for use by sync adapters + * This column is available for use by sync adapters. Column name. * <P>Type: TEXT</P> */ public static final String SYNC_DATA1 = "sync_data1"; /** - * The comments feed uri. + * The comments feed uri. Column name. + * TODO change to sync_data6 * <P>Type: TEXT</P> */ public static final String COMMENTS_URI = "commentsUri"; /** - * The time the event starts + * The time the event starts in UTC millis since epoch. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String DTSTART = "dtstart"; /** - * The time the event ends + * The time the event ends in UTC millis since epoch. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String DTEND = "dtend"; /** - * The duration of the event + * The duration of the event in RFC2445 format. Column name. * <P>Type: TEXT (duration in RFC2445 format)</P> */ public static final String DURATION = "duration"; /** - * The timezone for the event. - * <P>Type: TEXT + * The timezone for the event. Column name. + * <P>Type: TEXT</P> */ public static final String EVENT_TIMEZONE = "eventTimezone"; /** - * The timezone for the event, allDay events will have a local tz instead of UTC - * <P>Type: TEXT + * The timezone for the end time of the event. Column name. + * <P>Type: TEXT</P> */ public static final String EVENT_END_TIMEZONE = "eventEndTimezone"; /** - * Whether the event lasts all day or not + * Is the event all day (time zone independent). Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String ALL_DAY = "allDay"; /** * Defines how the event shows up for others when the calendar is - * shared. - * <P>Type: INTEGER</P> + * shared. Column name. + * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P> */ public static final String ACCESS_LEVEL = "accessLevel"; @@ -655,20 +745,20 @@ public final class Calendar { */ public static final int ACCESS_CONFIDENTIAL = 1; /** - * Private assumes the event appears as a free/busy slot with no - * details. + * Private shares the event as a free/busy slot with no details. */ public static final int ACCESS_PRIVATE = 2; /** - * Public assumes the contents are visible to anyone with access to the + * Public makes the contents visible to anyone with access to the * calendar. */ public static final int ACCESS_PUBLIC = 3; /** * If this event counts as busy time or is still free time that can be - * scheduled over. - * <P>Type: INTEGER</P> + * scheduled over. Column name. + * <P>Type: INTEGER (One of {@link #AVAILABILITY_BUSY}, + * {@link #AVAILABILITY_FREE})</P> */ public static final String AVAILABILITY = "availability"; @@ -684,45 +774,44 @@ public final class Calendar { public static final int AVAILABILITY_FREE = 1; /** - * Whether the event has an alarm or not + * Whether the event has an alarm or not. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String HAS_ALARM = "hasAlarm"; /** - * Whether the event has extended properties or not + * Whether the event has extended properties or not. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties"; /** - * The recurrence rule for the event. - * than one. + * The recurrence rule for the event. Column name. * <P>Type: TEXT</P> */ public static final String RRULE = "rrule"; /** - * The recurrence dates for the event. + * The recurrence dates for the event. Column name. * <P>Type: TEXT</P> */ public static final String RDATE = "rdate"; /** - * The recurrence exception rule for the event. + * The recurrence exception rule for the event. Column name. * <P>Type: TEXT</P> */ public static final String EXRULE = "exrule"; /** - * The recurrence exception dates for the event. + * The recurrence exception dates for the event. Column name. * <P>Type: TEXT</P> */ public static final String EXDATE = "exdate"; /** - * The _id of the original recurring event for which this event is an - * exception. + * The {@link Events#_ID} of the original recurring event for which this + * event is an exception. Column name. * <P>Type: TEXT</P> */ public static final String ORIGINAL_ID = "original_id"; @@ -730,27 +819,28 @@ public final class Calendar { /** * The _sync_id of the original recurring event for which this event is * an exception. The provider should keep the original_id in sync when - * this is updated. + * this is updated. Column name. * <P>Type: TEXT</P> */ public static final String ORIGINAL_SYNC_ID = "original_sync_id"; /** * The original instance time of the recurring event for which this - * event is an exception. + * event is an exception. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime"; /** * The allDay status (true or false) of the original recurring event - * for which this event is an exception. + * for which this event is an exception. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String ORIGINAL_ALL_DAY = "originalAllDay"; /** - * The last date this event repeats on, or NULL if it never ends + * The last date this event repeats on, or NULL if it never ends. Column + * name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String LAST_DATE = "lastDate"; @@ -758,61 +848,66 @@ public final class Calendar { /** * Whether the event has attendee information. True if the event * has full attendee data, false if the event has information about - * self only. + * self only. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String HAS_ATTENDEE_DATA = "hasAttendeeData"; /** - * Whether guests can modify the event. + * Whether guests can modify the event. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String GUESTS_CAN_MODIFY = "guestsCanModify"; /** - * Whether guests can invite other guests. + * Whether guests can invite other guests. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers"; /** - * Whether guests can see the list of attendees. + * Whether guests can see the list of attendees. Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests"; /** - * Email of the organizer (owner) of the event. + * Email of the organizer (owner) of the event. Column name. * <P>Type: STRING</P> */ public static final String ORGANIZER = "organizer"; /** - * Whether the user can invite others to the event. - * The GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary guest, - * while CAN_INVITE_OTHERS indicates if the user can invite others (either through - * GUESTS_CAN_INVITE_OTHERS or because the user has modify access to the event). + * Whether the user can invite others to the event. The + * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary + * guest, while CAN_INVITE_OTHERS indicates if the user can invite + * others (either through GUESTS_CAN_INVITE_OTHERS or because the user + * has modify access to the event). Column name. * <P>Type: INTEGER (boolean, readonly)</P> */ public static final String CAN_INVITE_OTHERS = "canInviteOthers"; /** * The owner account for this calendar, based on the calendar (foreign - * key into the calendars table). + * key into the calendars table). Column name. * <P>Type: String</P> */ public static final String OWNER_ACCOUNT = "ownerAccount"; /** - * Whether the row has been deleted. A deleted row should be ignored. + * Whether the row has been deleted. A deleted row should be ignored. + * Column name. * <P>Type: INTEGER (boolean)</P> */ public static final String DELETED = "deleted"; } /** - * Contains one entry per calendar event. Recurring events show up as a - * single entry. + * Class that represents an Event Entity. There is one entry per event. + * Recurring events show up as a single entry. This is a helper class to + * make batch operations easier. A {@link ContentResolver} or + * {@link ContentProviderClient} is required as the helper does additional + * queries to add reminders and attendees to each entry. */ public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns { /** @@ -821,10 +916,26 @@ public final class Calendar { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/event_entities"); + /** + * Creates a new iterator for events + * + * @param cursor An event query + * @param resolver For performing additional queries + * @return an EntityIterator containing one entity per event in the + * cursor + */ public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) { return new EntityIteratorImpl(cursor, resolver); } + /** + * Creates a new iterator for events + * + * @param cursor An event query + * @param provider For performing additional queries + * @return an EntityIterator containing one entity per event in the + * cursor + */ public static EntityIterator newEntityIterator(Cursor cursor, ContentProviderClient provider) { return new EntityIteratorImpl(cursor, provider); @@ -921,7 +1032,7 @@ public final class Calendar { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION); - DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3); @@ -1018,53 +1129,54 @@ public final class Calendar { } /** - * Contains one entry per calendar event. Recurring events show up as a single entry. + * Fields and helpers for interacting with Events. */ public static final class Events implements BaseColumns, SyncColumns, EventsColumns { - private static final String[] FETCH_ENTRY_COLUMNS = - new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID }; - - private static final String[] ATTENDEES_COLUMNS = - new String[] { AttendeesColumns.ATTENDEE_NAME, - AttendeesColumns.ATTENDEE_EMAIL, - AttendeesColumns.ATTENDEE_RELATIONSHIP, - AttendeesColumns.ATTENDEE_TYPE, - AttendeesColumns.ATTENDEE_STATUS }; - + /** + * Queries all events with the given projection. This is a blocking call + * and should not be done on the UI thread. + * + * @param cr The content resolver to use for the query + * @param projection The columns to return + * @return A Cursor containing all events in the db + */ public static final Cursor query(ContentResolver cr, String[] projection) { return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); } - public static final Cursor query(ContentResolver cr, String[] projection, - String where, String orderBy) { - return cr.query(CONTENT_URI, projection, where, - null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - private static String extractValue(ICalendar.Component component, - String propertyName) { - ICalendar.Property property = - component.getFirstProperty(propertyName); - if (property != null) { - return property.getValue(); - } - return null; + /** + * Queries events using the given projection, selection filter, and + * ordering. This is a blocking call and should not be done on the UI + * thread. For selection and selectionArgs usage see + * {@link ContentResolver#query(Uri, String[], String, String[], String)} + * + * @param cr The content resolver to use for the query + * @param projection The columns to return + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param orderBy How to order the rows as an SQL ORDER BY statement + * @return A Cursor containing the matching events + */ + public static final Cursor query(ContentResolver cr, String[] projection, String selection, + String[] selectionArgs, String orderBy) { + return cr.query(CONTENT_URI, projection, selection, null, + orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** - * The content:// style URL for this table + * The content:// style URL for interacting with events. Appending an + * event id using {@link ContentUris#withAppendedId(Uri, long)} will + * specify a single event. */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/events"); - public static final Uri DELETED_CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/deleted_events"); - /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = ""; + private static final String DEFAULT_SORT_ORDER = ""; /** * These are columns that should only ever be updated by the provider, @@ -1073,7 +1185,13 @@ public final class Calendar { */ public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, - ACCOUNT_TYPE + ACCOUNT_TYPE, + CAL_SYNC1, + CAL_SYNC2, + CAL_SYNC3, + CAL_SYNC4, + CAL_SYNC5, + CAL_SYNC6 }; /** @@ -1091,13 +1209,29 @@ public final class Calendar { } /** - * Contains one entry per calendar event instance. Recurring events show up every time - * they occur. + * Fields and helpers for interacting with Instances. An instance is a + * single occurrence of an event including time zone specific start and end + * days and minutes. */ public static final class Instances implements BaseColumns, EventsColumns, CalendarsColumns { private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=1"; + /** + * Performs a query to return all visible instances in the given range. + * This is a blocking function and should not be done on the UI thread. + * This will cause an expansion of recurring events to fill this time + * range if they are not already expanded and will slow down for larger + * time ranges with many recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @return A Cursor containing all instances in the given range + */ public static final Cursor query(ContentResolver cr, String[] projection, long begin, long end) { Uri.Builder builder = CONTENT_URI.buildUpon(); @@ -1107,111 +1241,184 @@ public final class Calendar { null, DEFAULT_SORT_ORDER); } + /** + * Performs a query to return all visible instances in the given range + * that match the given query. This is a blocking function and should + * not be done on the UI thread. This will cause an expansion of + * recurring events to fill this time range if they are not already + * expanded and will slow down for larger time ranges with many + * recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @param searchQuery A string of space separated search terms. Segments + * enclosed by double quotes will be treated as a single + * term. + * @return A Cursor of instances matching the search terms in the given + * time range + */ public static final Cursor query(ContentResolver cr, String[] projection, long begin, long end, String searchQuery) { Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, end); - return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED, - new String[] { searchQuery }, DEFAULT_SORT_ORDER); + builder = builder.appendPath(searchQuery); + return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED, null, + DEFAULT_SORT_ORDER); } - public static final Cursor query(ContentResolver cr, String[] projection, - long begin, long end, String where, String orderBy) { + /** + * Performs a query to return all visible instances in the given range + * that match the given selection. This is a blocking function and + * should not be done on the UI thread. This will cause an expansion of + * recurring events to fill this time range if they are not already + * expanded and will slow down for larger time ranges with many + * recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param orderBy How to order the rows as an SQL ORDER BY statement + * @return A Cursor of instances matching the selection + */ + public static final Cursor query(ContentResolver cr, String[] projection, long begin, + long end, String selection, String[] selectionArgs, String orderBy) { Uri.Builder builder = CONTENT_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, end); - if (TextUtils.isEmpty(where)) { - where = WHERE_CALENDARS_SELECTED; + if (TextUtils.isEmpty(selection)) { + selection = WHERE_CALENDARS_SELECTED; } else { - where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED; + selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; } - return cr.query(builder.build(), projection, where, - null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); + return cr.query(builder.build(), projection, selection, selectionArgs, + orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } + /** + * Performs a query to return all visible instances in the given range + * that match the given selection. This is a blocking function and + * should not be done on the UI thread. This will cause an expansion of + * recurring events to fill this time range if they are not already + * expanded and will slow down for larger time ranges with many + * recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @param searchQuery A string of space separated search terms. Segments + * enclosed by double quotes will be treated as a single + * term. + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param orderBy How to order the rows as an SQL ORDER BY statement + * @return A Cursor of instances matching the selection + */ public static final Cursor query(ContentResolver cr, String[] projection, long begin, - long end, String searchQuery, String where, String orderBy) { + long end, String searchQuery, String selection, String[] selectionArgs, + String orderBy) { Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, end); builder = builder.appendPath(searchQuery); - if (TextUtils.isEmpty(where)) { - where = WHERE_CALENDARS_SELECTED; + if (TextUtils.isEmpty(selection)) { + selection = WHERE_CALENDARS_SELECTED; } else { - where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED; + selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; } - return cr.query(builder.build(), projection, where, null, + return cr.query(builder.build(), projection, selection, selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** - * The content:// style URL for this table + * The content:// style URL for querying an instance range. The begin + * and end of the range to query should be added as path segments if + * this is used directly. */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/instances/when"); + /** + * The content:// style URL for querying an instance range by Julian + * Day. The start and end day should be added as path segments if this + * is used directly. + */ public static final Uri CONTENT_BY_DAY_URI = Uri.parse("content://" + AUTHORITY + "/instances/whenbyday"); + /** + * The content:// style URL for querying an instance range with a search + * term. The begin, end, and search string should be appended as path + * segments if this is used directly. + */ public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY + "/instances/search"); + /** + * The content:// style URL for querying an instance range with a search + * term. The start day, end day, and search string should be appended as + * path segments if this is used directly. + */ public static final Uri CONTENT_SEARCH_BY_DAY_URI = Uri.parse("content://" + AUTHORITY + "/instances/searchbyday"); /** * The default sort order for this table. */ - public static final String DEFAULT_SORT_ORDER = "begin ASC"; + private static final String DEFAULT_SORT_ORDER = "begin ASC"; /** - * The sort order is: events with an earlier start time occur - * first and if the start times are the same, then events with - * a later end time occur first. The later end time is ordered - * first so that long-running events in the calendar views appear - * first. If the start and end times of two events are - * the same then we sort alphabetically on the title. This isn't - * required for correctness, it just adds a nice touch. - */ - public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; - /** - * The beginning time of the instance, in UTC milliseconds + * The beginning time of the instance, in UTC milliseconds. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String BEGIN = "begin"; /** - * The ending time of the instance, in UTC milliseconds + * The ending time of the instance, in UTC milliseconds. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String END = "end"; /** - * The event for this instance + * The _id of the event for this instance. Column name. * <P>Type: INTEGER (long, foreign key to the Events table)</P> */ public static final String EVENT_ID = "event_id"; /** - * The Julian start day of the instance, relative to the local timezone + * The Julian start day of the instance, relative to the local time + * zone. Column name. * <P>Type: INTEGER (int)</P> */ public static final String START_DAY = "startDay"; /** - * The Julian end day of the instance, relative to the local timezone + * The Julian end day of the instance, relative to the local time + * zone. Column name. * <P>Type: INTEGER (int)</P> */ public static final String END_DAY = "endDay"; /** * The start minute of the instance measured from midnight in the - * local timezone. + * local time zone. Column name. * <P>Type: INTEGER (int)</P> */ public static final String START_MINUTE = "startMinute"; /** * The end minute of the instance measured from midnight in the - * local timezone. + * local time zone. Column name. * <P>Type: INTEGER (int)</P> */ public static final String END_MINUTE = "endMinute"; @@ -1219,14 +1426,12 @@ public final class Calendar { /** * CalendarCache stores some settings for calendar including the current - * time zone for the app. These settings are stored using a key/value + * time zone for the instaces. These settings are stored using a key/value * scheme. */ - public interface CalendarCacheColumns { + private interface CalendarCacheColumns { /** - * The key for the setting. Keys are defined in CalendarChache in the - * Calendar provider. - * TODO Add keys to this file + * The key for the setting. Keys are defined in {@link CalendarCache}. */ public static final String KEY = "key"; @@ -1296,7 +1501,7 @@ public final class Calendar { * the Instances table and these are all stored in the first (and only) * row of the CalendarMetaData table. */ - public interface CalendarMetaDataColumns { + private interface CalendarMetaDataColumns { /** * The local timezone that was used for precomputing the fields * in the Instances table. @@ -1330,34 +1535,51 @@ public final class Calendar { public static final String MAX_EVENTDAYS = "maxEventDays"; } + /** + * @hide + */ public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns { } - public interface EventDaysColumns { + private interface EventDaysColumns { /** - * The Julian starting day number. + * The Julian starting day number. Column name. * <P>Type: INTEGER (int)</P> */ public static final String STARTDAY = "startDay"; + /** + * The Julian ending day number. Column name. + * <P>Type: INTEGER (int)</P> + */ public static final String ENDDAY = "endDay"; } + /** + * Fields and helpers for querying for a list of days that contain events. + */ public static final class EventDays implements EventDaysColumns { - public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + - "/instances/groupbyday"); + private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + + "/instances/groupbyday"); + /** + * The projection used by the EventDays query. + */ public static final String[] PROJECTION = { STARTDAY, ENDDAY }; - public static final String SELECTION = "selected=1"; + private static final String SELECTION = "selected=1"; /** - * Retrieves the days with events for the Julian days starting at "startDay" - * for "numDays". + * Retrieves the days with events for the Julian days starting at + * "startDay" for "numDays". It returns a cursor containing startday and + * endday representing the max range of days for all events beginning on + * each startday.This is a blocking function and should not be done on + * the UI thread. * * @param cr the ContentResolver * @param startDay the first Julian day in the range * @param numDays the number of days to load (must be at least 1) - * @return a database cursor + * @return a database cursor containing a list of start and end days for + * events */ public static final Cursor query(ContentResolver cr, int startDay, int numDays) { if (numDays < 1) { @@ -1372,9 +1594,9 @@ public final class Calendar { } } - public interface RemindersColumns { + private interface RemindersColumns { /** - * The event the reminder belongs to + * The event the reminder belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> */ public static final String EVENT_ID = "event_id"; @@ -1382,17 +1604,24 @@ public final class Calendar { /** * The minutes prior to the event that the alarm should ring. -1 * specifies that we should use the default value for the system. + * Column name. * <P>Type: INTEGER</P> */ public static final String MINUTES = "minutes"; + /** + * Passing this as a minutes value will use the default reminder + * minutes. + */ public static final int MINUTES_DEFAULT = -1; /** - * The alarm method, as set on the server. DEFAULT, ALERT, EMAIL, and - * SMS are possible values; the device will only process DEFAULT and - * ALERT reminders (the other types are simply stored so we can send the - * same reminder info back to the server when we make changes). + * The alarm method, as set on the server. {@link #METHOD_DEFAULT}, + * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS} + * are possible values; the device will only process + * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the + * other types are simply stored so we can send the same reminder info + * back to the server when we make changes). */ public static final String METHOD = "method"; @@ -1402,61 +1631,85 @@ public final class Calendar { public static final int METHOD_SMS = 3; } + /** + * Fields and helpers for accessing reminders for an event. + */ public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns { - public static final String TABLE_NAME = "Reminders"; + private static final String REMINDERS_WHERE = Calendar.Reminders.EVENT_ID + "=?"; + /** + * The projection used by the reminders query. + */ + public static final String[] PROJECTION = new String[] { + _ID, MINUTES, METHOD,}; + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders"); + + /** + * Queries all reminders associated with the given event. This is a + * blocking call and should not be done on the UI thread. + * + * @param cr The content resolver to use for the query + * @param eventId The id of the event to retrieve reminders for + * @return A Cursor containing all reminders for the event + */ + public static final Cursor query(ContentResolver cr, long eventId) { + String[] remArgs = {Long.toString(eventId)}; + return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */, + null /* sort order */); + } } - public interface CalendarAlertsColumns { + private interface CalendarAlertsColumns { /** - * The event that the alert belongs to + * The event that the alert belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> */ public static final String EVENT_ID = "event_id"; /** - * The start time of the event, in UTC + * The start time of the event, in UTC. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String BEGIN = "begin"; /** - * The end time of the event, in UTC + * The end time of the event, in UTC. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String END = "end"; /** - * The alarm time of the event, in UTC + * The alarm time of the event, in UTC. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String ALARM_TIME = "alarmTime"; /** * The creation time of this database entry, in UTC. - * (Useful for debugging missed reminders.) + * Useful for debugging missed reminders. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String CREATION_TIME = "creationTime"; /** * The time that the alarm broadcast was received by the Calendar app, - * in UTC. (Useful for debugging missed reminders.) + * in UTC. Useful for debugging missed reminders. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String RECEIVED_TIME = "receivedTime"; /** * The time that the notification was created by the Calendar app, - * in UTC. (Useful for debugging missed reminders.) + * in UTC. Useful for debugging missed reminders. Column name. * <P>Type: INTEGER (long; millis since epoch)</P> */ public static final String NOTIFY_TIME = "notifyTime"; /** - * The state of this alert. It starts out as SCHEDULED, then when - * the alarm goes off, it changes to FIRED, and then when the user - * dismisses the alarm it changes to DISMISSED. + * The state of this alert. It starts out as {@link SCHEDULED}, then + * when the alarm goes off, it changes to {@link FIRED}, and then when + * the user dismisses the alarm it changes to {@link DISMISSED}. Column + * name. * <P>Type: INTEGER</P> */ public static final String STATE = "state"; @@ -1466,21 +1719,33 @@ public final class Calendar { public static final int DISMISSED = 2; /** - * The number of minutes that this alarm precedes the start time - * <P>Type: INTEGER </P> + * The number of minutes that this alarm precedes the start time. Column + * name. + * <P>Type: INTEGER</P> */ public static final String MINUTES = "minutes"; /** - * The default sort order for this table + * The default sort order for this alerts queries */ public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC"; } + /** + * Fields and helpers for accessing calendar alerts information. These + * fields are for tracking which alerts have been fired. + */ public static final class CalendarAlerts implements BaseColumns, CalendarAlertsColumns, EventsColumns, CalendarsColumns { + /** + * @hide + */ public static final String TABLE_NAME = "CalendarAlerts"; + /** + * The Uri for querying calendar alert information + */ + @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendar_alerts"); @@ -1507,6 +1772,11 @@ public final class Calendar { private static final boolean DEBUG = true; + /** + * Helper for inserting an alarm time associated with an event + * + * @hide + */ public static final Uri insert(ContentResolver cr, long eventId, long begin, long end, long alarmTime, int minutes) { ContentValues values = new ContentValues(); @@ -1523,6 +1793,19 @@ public final class Calendar { return cr.insert(CONTENT_URI, values); } + /** + * Queries alerts info using the given projection, selection filter, and + * ordering. This is a blocking call and should not be done on the UI + * thread. For selection and selectionArgs usage see + * {@link ContentResolver#query(Uri, String[], String, String[], String)} + * + * @param cr The content resolver to use for the query + * @param projection The columns to return + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param sortOrder How to order the rows as an SQL ORDER BY statement + * @return A Cursor containing the matching alerts + */ public static final Cursor query(ContentResolver cr, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return cr.query(CONTENT_URI, projection, selection, selectionArgs, @@ -1531,12 +1814,13 @@ public final class Calendar { /** * Finds the next alarm after (or equal to) the given time and returns - * the time of that alarm or -1 if no such alarm exists. + * the time of that alarm or -1 if no such alarm exists. This is a + * blocking call and should not be done on the UI thread. * * @param cr the ContentResolver * @param millis the time in UTC milliseconds * @return the next alarm time greater than or equal to "millis", or -1 - * if no such alarm exists. + * if no such alarm exists. */ public static final long findNextAlarmTime(ContentResolver cr, long millis) { String selection = ALARM_TIME + ">=" + millis; @@ -1619,6 +1903,17 @@ public final class Calendar { } } + /** + * Schedules an alarm intent with the system AlarmManager that will + * cause the Calendar provider to recheck alarms. This is used to wake + * the Calendar alarm handler when an alarm is expected or to do a + * periodic refresh of alarm data. + * + * @param context A context for referencing system resources + * @param manager The AlarmManager to use or null + * @param alarmTime The time to fire the intent in UTC millis since + * epoch + */ public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) { if (DEBUG) { Time time = new Time(); @@ -1678,27 +1973,32 @@ public final class Calendar { } } - public interface ExtendedPropertiesColumns { + private interface ExtendedPropertiesColumns { /** - * The event the extended property belongs to + * The event the extended property belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> */ public static final String EVENT_ID = "event_id"; /** * The name of the extended property. This is a uri of the form - * {scheme}#{local-name} convention. + * {scheme}#{local-name} convention. Column name. * <P>Type: TEXT</P> */ public static final String NAME = "name"; /** - * The value of the extended property. + * The value of the extended property. Column name. * <P>Type: TEXT</P> */ public static final String VALUE = "value"; } + /** + * Fields for accessing the Extended Properties. This is a generic set of + * name/value pairs for use by sync adapters or apps to add extra + * information to events. + */ public static final class ExtendedProperties implements BaseColumns, ExtendedPropertiesColumns, EventsColumns { public static final Uri CONTENT_URI = @@ -1719,7 +2019,7 @@ public final class Calendar { */ private SyncState() {} - public static final String CONTENT_DIRECTORY = + private static final String CONTENT_DIRECTORY = SyncStateContract.Constants.CONTENT_DIRECTORY; /** @@ -1732,39 +2032,43 @@ public final class Calendar { /** * Columns from the EventsRawTimes table */ - public interface EventsRawTimesColumns { + private interface EventsRawTimesColumns { /** - * The corresponding event id + * The corresponding event id. Column name. * <P>Type: INTEGER (long)</P> */ public static final String EVENT_ID = "event_id"; /** - * The RFC2445 compliant time the event starts + * The RFC2445 compliant time the event starts. Column name. * <P>Type: TEXT</P> */ public static final String DTSTART_2445 = "dtstart2445"; /** - * The RFC2445 compliant time the event ends + * The RFC2445 compliant time the event ends. Column name. * <P>Type: TEXT</P> */ public static final String DTEND_2445 = "dtend2445"; /** - * The RFC2445 compliant original instance time of the recurring event for which this - * event is an exception. + * The RFC2445 compliant original instance time of the recurring event + * for which this event is an exception. Column name. * <P>Type: TEXT</P> */ public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445"; /** - * The RFC2445 compliant last date this event repeats on, or NULL if it never ends + * The RFC2445 compliant last date this event repeats on, or NULL if it + * never ends. Column name. * <P>Type: TEXT</P> */ public static final String LAST_DATE_2445 = "lastDate2445"; } + /** + * @hide + */ public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns { } } diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java index 3ef8293..4ae21ad 100644 --- a/core/java/android/util/FinitePool.java +++ b/core/java/android/util/FinitePool.java @@ -69,6 +69,7 @@ class FinitePool<T extends Poolable<T>> implements Pool<T> { if (element != null) { element.setNextPoolable(null); + element.setPooled(false); mManager.onAcquired(element); } @@ -76,9 +77,13 @@ class FinitePool<T extends Poolable<T>> implements Pool<T> { } public void release(T element) { + if (element.isPooled()) { + throw new IllegalArgumentException("Element already in the pool."); + } if (mInfinite || mPoolCount < mLimit) { mPoolCount++; element.setNextPoolable(mRoot); + element.setPooled(true); mRoot = element; } mManager.onReleased(element); diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java index fd9bd9b..87e0529 100644 --- a/core/java/android/util/Poolable.java +++ b/core/java/android/util/Poolable.java @@ -22,4 +22,6 @@ package android.util; public interface Poolable<T> { void setNextPoolable(T element); T getNextPoolable(); + boolean isPooled(); + void setPooled(boolean isPooled); } diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index b2a35d3..ba06795 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -109,16 +109,16 @@ public class Gravity */ public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000; - /** Push object to x-axis position before its container, not changing its size. */ - public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT; + /** Push object to x-axis position at the start of its container, not changing its size. */ + public static final int START = RELATIVE_HORIZONTAL_DIRECTION | LEFT; - /** Push object to x-axis position after its container, not changing its size. */ - public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT; + /** Push object to x-axis position at the end of its container, not changing its size. */ + public static final int END = RELATIVE_HORIZONTAL_DIRECTION | RIGHT; /** * Binary mask for the horizontal gravity and script specific direction bit. */ - public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER; + public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END; /** * Apply a gravity constant to an object. This suppose that the layout direction is LTR. @@ -342,8 +342,8 @@ public class Gravity /** * <p>Convert script specific gravity to absolute horizontal value.</p> * - * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT. - * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT. + * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT. + * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT. * * @param gravity The gravity to convert to absolute (horizontal) values. * @param isRtl Whether the layout is right-to-left. @@ -351,11 +351,11 @@ public class Gravity */ public static int getAbsoluteGravity(int gravity, boolean isRtl) { int result = gravity; - // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER) + // If layout is script specific and gravity is horizontal relative (START or END) if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) { - if ((result & Gravity.BEFORE) == Gravity.BEFORE) { - // Remove the BEFORE bit - result &= ~BEFORE; + if ((result & Gravity.START) == Gravity.START) { + // Remove the START bit + result &= ~START; if (isRtl) { // Set the RIGHT bit result |= RIGHT; @@ -363,9 +363,9 @@ public class Gravity // Set the LEFT bit result |= LEFT; } - } else if ((result & Gravity.AFTER) == Gravity.AFTER) { - // Remove the AFTER bit - result &= ~AFTER; + } else if ((result & Gravity.END) == Gravity.END) { + // Remove the END bit + result &= ~END; if (isRtl) { // Set the LEFT bit result |= LEFT; diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index fccef2b..5a91d31 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -50,6 +50,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { private int mPtr; private VelocityTracker mNext; + private boolean mIsPooled; private static native int nativeInitialize(); private static native void nativeDispose(int ptr); @@ -93,6 +94,20 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { return mNext; } + /** + * @hide + */ + public boolean isPooled() { + return mIsPooled; + } + + /** + * @hide + */ + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } + private VelocityTracker() { mPtr = nativeInitialize(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 014f19f..51eb13b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -61,6 +61,7 @@ import android.view.ContextMenu.ContextMenuInfo; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; @@ -1492,6 +1493,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit private static final Object sTagsLock = new Object(); /** + * The next available accessiiblity id. + */ + private static int sNextAccessibilityViewId; + + /** * The animation currently associated with this view. * @hide */ @@ -1533,6 +1539,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit int mID = NO_ID; /** + * The stable ID of this view for accessibility porposes. + */ + int mAccessibilityViewId = NO_ID; + + /** * The view's tag. * {@hide} * @@ -3649,6 +3660,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + event.setSource(this); event.setClassName(getClass().getName()); event.setPackageName(getContext().getPackageName()); event.setEnabled(isEnabled()); @@ -3664,6 +3676,112 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** + * Returns an {@link AccessibilityNodeInfo} representing this view from the + * point of view of an {@link android.accessibilityservice.AccessibilityService}. + * This method is responsible for obtaining an accessibility node info from a + * pool of reusable instances and calling + * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on this view to + * initialize the former. + * <p> + * Note: The client is responsible for recycling the obtained instance by calling + * {@link AccessibilityNodeInfo#recycle()} to minimize object creation. + * </p> + * @return A populated {@link AccessibilityNodeInfo}. + * + * @see AccessibilityNodeInfo + */ + public AccessibilityNodeInfo createAccessibilityNodeInfo() { + AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this); + onInitializeAccessibilityNodeInfo(info); + return info; + } + + /** + * Initializes an {@link AccessibilityNodeInfo} with information about this view. + * The base implementation sets: + * <ul> + * <li>{@link AccessibilityNodeInfo#setParent(View)},</li> + * <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li> + * <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li> + * <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li> + * <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li> + * <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},</li> + * <li>{@link AccessibilityNodeInfo#setClickable(boolean)},</li> + * <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},</li> + * <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li> + * <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li> + * <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li> + * </ul> + * <p> + * Subclasses should override this method, call the super implementation, + * and set additional attributes. + * </p> + * @param info The instance to initialize. + */ + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + Rect bounds = mAttachInfo.mTmpInvalRect; + getDrawingRect(bounds); + info.setBounds(bounds); + + ViewParent parent = getParent(); + if (parent instanceof View) { + View parentView = (View) parent; + info.setParent(parentView); + } + + info.setPackageName(mContext.getPackageName()); + info.setClassName(getClass().getName()); + info.setContentDescription(getContentDescription()); + + info.setEnabled(isEnabled()); + info.setClickable(isClickable()); + info.setFocusable(isFocusable()); + info.setFocused(isFocused()); + info.setSelected(isSelected()); + info.setLongClickable(isLongClickable()); + + // TODO: These make sense only if we are in an AdapterView but all + // views can be selected. Maybe from accessiiblity perspective + // we should report as selectable view in an AdapterView. + info.addAction(AccessibilityNodeInfo.ACTION_SELECT); + info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); + + if (isFocusable()) { + if (isFocused()) { + info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); + } else { + info.addAction(AccessibilityNodeInfo.ACTION_FOCUS); + } + } + } + + /** + * Gets the unique identifier of this view on the screen for accessibility purposes. + * If this {@link View} is not attached to any window, {@value #NO_ID} is returned. + * + * @return The view accessibility id. + * + * @hide + */ + public int getAccessibilityViewId() { + if (mAccessibilityViewId == NO_ID) { + mAccessibilityViewId = sNextAccessibilityViewId++; + } + return mAccessibilityViewId; + } + + /** + * Gets the unique identifier of the window in which this View reseides. + * + * @return The window accessibility id. + * + * @hide + */ + public int getAccessibilityWindowId() { + return mAttachInfo != null ? mAttachInfo.mAccessibilityWindowId : NO_ID; + } + + /** * Gets the {@link View} description. It briefly describes the view and is * primarily used for accessibility support. Set this property to enable * better accessibility support for your application. This is especially @@ -4571,6 +4689,16 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** + * Finds the Views that contain given text. The containment is case insensitive. + * As View's text is considered any text content that View renders. + * + * @param outViews The output list of matching Views. + * @param text The text to match against. + */ + public void findViewsWithText(ArrayList<View> outViews, CharSequence text) { + } + + /** * Find and return all touchable views that are descendants of this view, * possibly including this view if it is touchable itself. * @@ -4677,8 +4805,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // need to be focusable in touch mode if in touch mode if (isInTouchMode() && - (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) { - return false; + (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) { + return false; } // need to not have any parents blocking us @@ -12719,6 +12847,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit ); private InvalidateInfo mNext; + private boolean mIsPooled; View target; @@ -12742,6 +12871,14 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit void release() { sPool.release(this); } + + public boolean isPooled() { + return mIsPooled; + } + + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } } final IWindowSession mSession; @@ -12952,6 +13089,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24); /** + * The id of the window for accessibility purposes. + */ + int mAccessibilityWindowId = View.NO_ID; + + /** * Creates a new set of attachment information with the specified * events handler and thread. * diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 1e72529..9ab4c82 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -50,18 +50,28 @@ import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; +import android.util.Pool; +import android.util.Poolable; +import android.util.PoolableManager; +import android.util.Pools; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityInteractionConnection; +import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; + import com.android.internal.policy.PolicyManager; +import com.android.internal.util.Predicate; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; @@ -71,6 +81,7 @@ import java.io.IOException; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -248,6 +259,12 @@ public final class ViewAncestor extends Handler implements ViewParent, */ AudioManager mAudioManager; + final AccessibilityManager mAccessibilityManager; + + AccessibilityInteractionController mAccessibilityInteractionContrtoller; + + AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; + private final int mDensity; /** @@ -285,7 +302,7 @@ public final class ViewAncestor extends Handler implements ViewParent, // done here instead of in the static block because Zygote does not // allow the spawning of threads. getWindowSession(context.getMainLooper()); - + mThread = Thread.currentThread(); mLocation = new WindowLeaked(null); mLocation.fillInStackTrace(); @@ -302,6 +319,11 @@ public final class ViewAncestor extends Handler implements ViewParent, mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added mAdded = false; + mAccessibilityManager = AccessibilityManager.getInstance(context); + mAccessibilityInteractionConnectionManager = + new AccessibilityInteractionConnectionManager(); + mAccessibilityManager.addAccessibilityStateChangeListener( + mAccessibilityInteractionConnectionManager); mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); mViewConfiguration = ViewConfiguration.get(context); mDensity = context.getResources().getDisplayMetrics().densityDpi; @@ -490,10 +512,14 @@ public final class ViewAncestor extends Handler implements ViewParent, InputQueue.registerInputChannel(mInputChannel, mInputHandler, Looper.myQueue()); } - + view.assignParent(this); mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0; mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0; + + if (mAccessibilityManager.isEnabled()) { + mAccessibilityInteractionConnectionManager.ensureConnection(); + } } } } @@ -2004,6 +2030,10 @@ public final class ViewAncestor extends Handler implements ViewParent, mView.dispatchDetachedFromWindow(); } + mAccessibilityInteractionConnectionManager.ensureNoConnection(); + mAccessibilityManager.removeAccessibilityStateChangeListener( + mAccessibilityInteractionConnectionManager); + mView = null; mAttachInfo.mRootView = null; mAttachInfo.mSurface = null; @@ -2020,7 +2050,6 @@ public final class ViewAncestor extends Handler implements ViewParent, InputQueue.unregisterInputChannel(mInputChannel); } } - try { sWindowSession.remove(mWindow); } catch (RemoteException e) { @@ -2098,6 +2127,10 @@ public final class ViewAncestor extends Handler implements ViewParent, public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; public final static int DISPATCH_GENERIC_MOTION = 1018; + public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1019; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1020; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1021; + public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1022; @Override public void handleMessage(Message msg) { @@ -2298,9 +2331,25 @@ public final class ViewAncestor extends Handler implements ViewParent, case DISPATCH_SYSTEM_UI_VISIBILITY: { handleDispatchSystemUiVisibilityChanged(msg.arg1); } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); + } break; + case DO_PERFORM_ACCESSIBILITY_ACTION: { + getAccessibilityInteractionController() + .perfromAccessibilityActionUiThread(msg); + } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByViewIdUiThread(msg); + } break; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: { + getAccessibilityInteractionController() + .findAccessibilityNodeInfosByViewTextUiThread(msg); + } break; } } - + private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { if (mFinishedCallback != null) { Slog.w(TAG, "Received a new input event from the input queue but there is " @@ -3220,6 +3269,17 @@ public final class ViewAncestor extends Handler implements ViewParent, return mAudioManager; } + public AccessibilityInteractionController getAccessibilityInteractionController() { + if (mView == null) { + throw new IllegalStateException("getAccessibilityInteractionController" + + " called when there is no mView"); + } + if (mAccessibilityInteractionContrtoller == null) { + mAccessibilityInteractionContrtoller = new AccessibilityInteractionController(); + } + return mAccessibilityInteractionContrtoller; + } + private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { @@ -3517,7 +3577,7 @@ public final class ViewAncestor extends Handler implements ViewParent, * send an {@link AccessibilityEvent} to announce that. */ private void sendAccessibilityEvents() { - if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) { + if (!mAccessibilityManager.isEnabled()) { return; } mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); @@ -3545,7 +3605,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mView == null) { return false; } - AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event); + mAccessibilityManager.sendAccessibilityEvent(event); return true; } @@ -3608,18 +3668,18 @@ public final class ViewAncestor extends Handler implements ViewParent, static class InputMethodCallback extends IInputMethodCallback.Stub { private WeakReference<ViewAncestor> mViewAncestor; - public InputMethodCallback(ViewAncestor viewRoot) { - mViewAncestor = new WeakReference<ViewAncestor>(viewRoot); + public InputMethodCallback(ViewAncestor viewAncestor) { + mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); } public void finishedEvent(int seq, boolean handled) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchFinishedEvent(seq, handled); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchFinishedEvent(seq, handled); } } - public void sessionCreated(IInputMethodSession session) throws RemoteException { + public void sessionCreated(IInputMethodSession session) { // Stub -- not for use in the client. } } @@ -3627,36 +3687,37 @@ public final class ViewAncestor extends Handler implements ViewParent, static class W extends IWindow.Stub { private final WeakReference<ViewAncestor> mViewAncestor; - W(ViewAncestor viewRoot) { - mViewAncestor = new WeakReference<ViewAncestor>(viewRoot); + W(ViewAncestor viewAncestor) { + mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, + newConfig); } } public void dispatchAppVisibility(boolean visible) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchAppVisibility(visible); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchAppVisibility(visible); } } public void dispatchGetNewSurface() { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchGetNewSurface(); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchGetNewSurface(); } } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.windowFocusChanged(hasFocus, inTouchMode); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.windowFocusChanged(hasFocus, inTouchMode); } } @@ -3674,9 +3735,9 @@ public final class ViewAncestor extends Handler implements ViewParent, } public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - final View view = viewRoot.mView; + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + final View view = viewAncestor.mView; if (view != null) { if (checkCallingPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -3705,9 +3766,9 @@ public final class ViewAncestor extends Handler implements ViewParent, } public void closeSystemDialogs(String reason) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchCloseSystemDialogs(reason); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchCloseSystemDialogs(reason); } } @@ -3720,7 +3781,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } } } - + public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync) { if (sync) { @@ -3733,17 +3794,16 @@ public final class ViewAncestor extends Handler implements ViewParent, /* Drag/drop */ public void dispatchDragEvent(DragEvent event) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchDragEvent(event); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchDragEvent(event); } } - @Override public void dispatchSystemUiVisibilityChanged(int visibility) { - final ViewAncestor viewRoot = mViewAncestor.get(); - if (viewRoot != null) { - viewRoot.dispatchSystemUiVisibilityChanged(visibility); + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchSystemUiVisibilityChanged(visibility); } } } @@ -4053,5 +4113,395 @@ public final class ViewAncestor extends Handler implements ViewParent, } } + /** + * Class for managing the accessibility interaction connection + * based on the global accessibility state. + */ + final class AccessibilityInteractionConnectionManager + implements AccessibilityStateChangeListener { + public void onAccessibilityStateChanged(boolean enabled) { + if (enabled) { + ensureConnection(); + } else { + ensureNoConnection(); + } + } + + public void ensureConnection() { + final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; + if (!registered) { + mAttachInfo.mAccessibilityWindowId = + mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, + new AccessibilityInteractionConnection(ViewAncestor.this)); + } + } + + public void ensureNoConnection() { + final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; + if (registered) { + mAttachInfo.mAccessibilityWindowId = View.NO_ID; + mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); + } + } + } + + /** + * This class is an interface this ViewAncestor provides to the + * AccessibilityManagerService to the latter can interact with + * the view hierarchy in this ViewAncestor. + */ + final class AccessibilityInteractionConnection + extends IAccessibilityInteractionConnection.Stub { + private final WeakReference<ViewAncestor> mViewAncestor; + + AccessibilityInteractionConnection(ViewAncestor viewAncestor) { + mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); + } + + public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor == null) { + return; + } + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId, + interactionId, callback); + } + + public void performAccessibilityAction(int accessibilityId, int action, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor == null) { + return; + } + getAccessibilityInteractionController() + .performAccessibilityActionClientThread(accessibilityId, action, interactionId, + callback); + } + + public void findAccessibilityNodeInfoByViewId(int viewId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor == null) { + return; + } + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback); + } + + public void findAccessibilityNodeInfosByViewText(String text, int interactionId, + IAccessibilityInteractionConnectionCallback callback) { + final ViewAncestor viewAncestor = mViewAncestor.get(); + if (viewAncestor == null) { + return; + } + getAccessibilityInteractionController() + .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId, callback); + } + } + + /** + * Class for managing accessibility interactions initiated from the system + * and targeting the view hierarchy. A *ClientThread method is to be + * called from the interaction connection this ViewAncestor gives the + * system to talk to it and a corresponding *UiThread method that is executed + * on the UI thread. + */ + final class AccessibilityInteractionController { + private static final int POOL_SIZE = 5; + + private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate = + new FindByAccessibilitytIdPredicate(); + + private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = + new ArrayList<AccessibilityNodeInfo>(); + + // Reusable poolable arguments for interacting with the view hierarchy + // to fit more arguments than Message and to avoid sharing objects between + // two messages since several threads can send messages concurrently. + private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool( + new PoolableManager<SomeArgs>() { + public SomeArgs newInstance() { + return new SomeArgs(); + } + + public void onAcquired(SomeArgs info) { + /* do nothing */ + } + + public void onReleased(SomeArgs info) { + info.clear(); + } + }, POOL_SIZE) + ); + + public class SomeArgs implements Poolable<SomeArgs> { + private SomeArgs mNext; + private boolean mIsPooled; + + public Object arg1; + public Object arg2; + public int argi1; + public int argi2; + public int argi3; + + public SomeArgs getNextPoolable() { + return mNext; + } + + public boolean isPooled() { + return mIsPooled; + } + + public void setNextPoolable(SomeArgs args) { + mNext = args; + } + + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } + + private void clear() { + arg1 = null; + arg2 = null; + argi1 = 0; + argi2 = 0; + argi3 = 0; + } + } + + public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; + message.arg1 = accessibilityId; + message.arg2 = interactionId; + message.obj = callback; + sendMessage(message); + } + + public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { + final int accessibilityId = message.arg1; + final int interactionId = message.arg2; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) message.obj; + + View root = ViewAncestor.this.mView; + if (root == null) { + return; + } + + FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; + predicate.init(accessibilityId); + + View target = root.findViewByPredicate(predicate); + if (target != null) { + AccessibilityNodeInfo info = target.createAccessibilityNodeInfo(); + try { + callback.setFindAccessibilityNodeInfoResult(info, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; + message.arg1 = viewId; + message.arg2 = interactionId; + message.obj = callback; + sendMessage(message); + } + + public void findAccessibilityNodeInfoByViewIdUiThread(Message message) { + final int viewId = message.arg1; + final int interactionId = message.arg2; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) message.obj; + + View root = ViewAncestor.this.mView; + if (root == null) { + return; + } + View target = root.findViewById(viewId); + if (target != null) { + AccessibilityNodeInfo info = target.createAccessibilityNodeInfo(); + try { + callback.setFindAccessibilityNodeInfoResult(info, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + + public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId, + IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT; + SomeArgs args = mPool.acquire(); + args.arg1 = text; + args.argi1 = interactionId; + args.arg2 = callback; + message.obj = args; + sendMessage(message); + } + + public void findAccessibilityNodeInfosByViewTextUiThread(Message message) { + SomeArgs args = (SomeArgs) message.obj; + final String text = (String) args.arg1; + final int interactionId = args.argi1; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) args.arg2; + mPool.release(args); + + View root = ViewAncestor.this.mView; + if (root == null) { + return; + } + + ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList; + foundViews.clear(); + + root.findViewsWithText(foundViews, text); + if (foundViews.isEmpty()) { + return; + } + + List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; + infos.clear(); + + final int viewCount = foundViews.size(); + for (int i = 0; i < viewCount; i++) { + View foundView = foundViews.get(i); + infos.add(foundView.createAccessibilityNodeInfo()); + } + + try { + callback.setFindAccessibilityNodeInfosResult(infos, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + + public void performAccessibilityActionClientThread(int accessibilityId, int action, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { + Message message = Message.obtain(); + message.what = DO_PERFORM_ACCESSIBILITY_ACTION; + SomeArgs args = mPool.acquire(); + args.argi1 = accessibilityId; + args.argi2 = action; + args.argi3 = interactionId; + args.arg1 = callback; + message.obj = args; + sendMessage(message); + } + + public void perfromAccessibilityActionUiThread(Message message) { + SomeArgs args = (SomeArgs) message.obj; + final int accessibilityId = args.argi1; + final int action = args.argi2; + final int interactionId = args.argi3; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) args.arg1; + mPool.release(args); + + if (ViewAncestor.this.mView == null) { + return; + } + + boolean succeeded = false; + switch (action) { + case AccessibilityNodeInfo.ACTION_FOCUS: { + succeeded = performActionFocus(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: { + succeeded = performActionClearFocus(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_SELECT: { + succeeded = performActionSelect(accessibilityId); + } break; + case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: { + succeeded = performActionClearSelection(accessibilityId); + } break; + } + + try { + callback.setPerformAccessibilityActionResult(succeeded, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + + private boolean performActionFocus(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + // Get out of touch mode since accessibility wants to move focus around. + ensureTouchMode(false); + return target.requestFocus(); + } + + private boolean performActionClearFocus(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (!target.isFocused()) { + return false; + } + target.clearFocus(); + return !target.isFocused(); + } + + private boolean performActionSelect(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (target.isSelected()) { + return false; + } + target.setSelected(true); + return target.isSelected(); + } + + private boolean performActionClearSelection(int accessibilityId) { + View target = findViewByAccessibilityId(accessibilityId); + if (target == null) { + return false; + } + if (!target.isSelected()) { + return false; + } + target.setSelected(false); + return !target.isSelected(); + } + + private View findViewByAccessibilityId(int accessibilityId) { + View root = ViewAncestor.this.mView; + if (root == null) { + return null; + } + mFindByAccessibilityIdPredicate.init(accessibilityId); + return root.findViewByPredicate(mFindByAccessibilityIdPredicate); + } + + private final class FindByAccessibilitytIdPredicate implements Predicate<View> { + public int mSerchedId; + + public void init(int searchedId) { + mSerchedId = searchedId; + } + + public boolean apply(View view) { + return (view.getAccessibilityViewId() == mSerchedId); + } + } + } + private static native void nativeShowFPS(Canvas canvas, int durationMillis); } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f504b90..57ee8a0 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -35,6 +35,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -772,6 +773,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + @Override + public void findViewsWithText(ArrayList<View> outViews, CharSequence text) { + final int childrenCount = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child = children[i]; + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { + child.findViewsWithText(outViews, text); + } + } + } + /** * {@inheritDoc} */ @@ -2007,6 +2020,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return false; } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + + for (int i = 0, count = mChildrenCount; i < count; i++) { + View child = mChildren[i]; + info.addChild(child); + } + } + /** * {@inheritDoc} */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index a1ddd08..b0181bb 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -77,8 +77,8 @@ public interface WindowManager extends ViewManager { implements Parcelable { /** * X position for this window. With the default gravity it is ignored. - * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or - * {@link Gravity#AFTER} it provides an offset from the given edge. + * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or + * {@link Gravity#END} it provides an offset from the given edge. */ @ViewDebug.ExportedProperty public int x; diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 7b80797..32bfa2f 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -16,9 +16,12 @@ package android.view.accessibility; +import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; import android.text.TextUtils; +import android.view.View; import java.util.ArrayList; @@ -159,6 +162,7 @@ import java.util.ArrayList; * @see android.accessibilityservice.AccessibilityService */ public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { + private static final boolean DEBUG = false; /** * Invalid selection/focus position. @@ -256,21 +260,38 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private static final Object sPoolLock = new Object(); private static AccessibilityEvent sPool; private static int sPoolSize; - private AccessibilityEvent mNext; private boolean mIsInPool; private int mEventType; + private int mSourceAccessibilityViewId; + private int mSourceAccessibilityWindowId; private CharSequence mPackageName; private long mEventTime; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); + private IAccessibilityServiceConnection mConnection; + /* * Hide constructor from clients. */ private AccessibilityEvent() { + } + /** + * Initialize an event from another one. + * + * @param event The event to initialize from. + */ + void init(AccessibilityEvent event) { + super.init(event); + mEventType = event.mEventType; + mEventTime = event.mEventTime; + mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId; + mSourceAccessibilityViewId = event.mSourceAccessibilityViewId; + mPackageName = event.mPackageName; + mConnection = event.mConnection; } /** @@ -286,8 +307,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Appends an {@link AccessibilityRecord} to the end of event records. * * @param record The record to append. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void appendRecord(AccessibilityRecord record) { + enforceNotSealed(); mRecords.add(record); } @@ -311,11 +335,89 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** + * Sets the event source. + * + * @param source The source. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setSource(View source) { + enforceNotSealed(); + if (source != null) { + mSourceAccessibilityWindowId = source.getAccessibilityWindowId(); + mSourceAccessibilityViewId = source.getAccessibilityViewId(); + } else { + mSourceAccessibilityWindowId = View.NO_ID; + mSourceAccessibilityViewId = View.NO_ID; + } + } + + /** + * Gets the {@link AccessibilityNodeInfo} of the event source. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * @return The info. + */ + public AccessibilityNodeInfo getSource() { + enforceSealed(); + if (mSourceAccessibilityWindowId == View.NO_ID + || mSourceAccessibilityViewId == View.NO_ID) { + return null; + } + try { + return mConnection.findAccessibilityNodeInfoByAccessibilityId( + mSourceAccessibilityWindowId, mSourceAccessibilityViewId); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the id of the window from which the event comes from. + * + * @return The window id. + */ + public int getAccessibilityWindowId() { + return mSourceAccessibilityWindowId; + } + + /** + * Sets the client token for the accessibility service that + * provided this node info. + * + * @param connection The connection. + * + * @hide + */ + public final void setConnection(IAccessibilityServiceConnection connection) { + mConnection = connection; + } + + /** + * Gets the accessibility window id of the source window. + * + * @return The id. + * + * @hide + */ + public int getSourceAccessibilityWindowId() { + return mSourceAccessibilityWindowId; + } + + /** * Sets the event type. * * @param eventType The event type. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setEventType(int eventType) { + enforceNotSealed(); mEventType = eventType; } @@ -332,8 +434,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Sets the time in which this event was sent. * * @param eventTime The event time. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setEventTime(long eventTime) { + enforceNotSealed(); mEventTime = eventTime; } @@ -350,8 +455,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Sets the package name of the source. * * @param packageName The package name. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setPackageName(CharSequence packageName) { + enforceNotSealed(); mPackageName = packageName; } @@ -370,6 +478,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is + * instantiated with type property set. + * + * @param event The other event. + * @return An instance. + */ + public static AccessibilityEvent obtain(AccessibilityEvent event) { + AccessibilityEvent eventClone = AccessibilityEvent.obtain(); + eventClone.init(event); + + final int recordCount = event.mRecords.size(); + for (int i = 0; i < recordCount; i++) { + AccessibilityRecord record = event.mRecords.get(i); + AccessibilityRecord recordClone = AccessibilityRecord.obtain(record); + eventClone.mRecords.add(recordClone); + } + + return eventClone; + } + + /** + * Returns a cached instance if such is available or a new one is * instantiated. * * @return An instance. @@ -413,11 +542,16 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Clears the state of this instance. + * + * @hide */ @Override protected void clear() { super.clear(); + mConnection = null; mEventType = 0; + mSourceAccessibilityViewId = View.NO_ID; + mSourceAccessibilityWindowId = View.NO_ID; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { @@ -432,7 +566,14 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. */ public void initFromParcel(Parcel parcel) { + if (parcel.readInt() == 1) { + mConnection = IAccessibilityServiceConnection.Stub.asInterface( + parcel.readStrongBinder()); + } + setSealed(parcel.readInt() == 1); mEventType = parcel.readInt(); + mSourceAccessibilityWindowId = parcel.readInt(); + mSourceAccessibilityViewId = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); readAccessibilityRecordFromParcel(this, parcel); @@ -471,7 +612,16 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * {@inheritDoc} */ public void writeToParcel(Parcel parcel, int flags) { + if (mConnection == null) { + parcel.writeInt(0); + } else { + parcel.writeInt(1); + parcel.writeStrongBinder(mConnection.asBinder()); + } + parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); + parcel.writeInt(mSourceAccessibilityWindowId); + parcel.writeInt(mSourceAccessibilityViewId); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); writeAccessibilityRecordToParcel(this, parcel, flags); @@ -519,18 +669,36 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("; EventType: ").append(eventTypeToString(mEventType)); builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); - builder.append(" \n{\n"); builder.append(super.toString()); - builder.append("\n"); - for (int i = 0; i < mRecords.size(); i++) { - AccessibilityRecord record = mRecords.get(i); - builder.append(" Record "); - builder.append(i); - builder.append(":"); - builder.append(record.toString()); + if (DEBUG) { builder.append("\n"); + builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId); + builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId); + for (int i = 0; i < mRecords.size(); i++) { + AccessibilityRecord record = mRecords.get(i); + builder.append(" Record "); + builder.append(i); + builder.append(":"); + builder.append(" [ ClassName: " + record.mClassName); + builder.append("; Text: " + record.mText); + builder.append("; ContentDescription: " + record.mContentDescription); + builder.append("; ItemCount: " + record.mItemCount); + builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex); + builder.append("; IsEnabled: " + record.isEnabled()); + builder.append("; IsPassword: " + record.isPassword()); + builder.append("; IsChecked: " + record.isChecked()); + builder.append("; IsFullScreen: " + record.isFullScreen()); + builder.append("; BeforeText: " + record.mBeforeText); + builder.append("; FromIndex: " + record.mFromIndex); + builder.append("; AddedCount: " + record.mAddedCount); + builder.append("; RemovedCount: " + record.mRemovedCount); + builder.append("; ParcelableData: " + record.mParcelableData); + builder.append(" ]"); + builder.append("\n"); + } + } else { + builder.append("; recordCount: ").append(getAddedCount()); } - builder.append("}\n"); return builder.toString(); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 88f8878..eece64a 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -28,10 +28,13 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.util.Log; +import android.view.IWindow; +import android.view.View; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. @@ -62,6 +65,21 @@ public final class AccessibilityManager { boolean mIsEnabled; + final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners = + new CopyOnWriteArrayList<AccessibilityStateChangeListener>(); + + /** + * Listener for the accessibility state. + */ + public interface AccessibilityStateChangeListener { + /** + * Called back on change in the accessibility state. + * + * @param enabled + */ + public void onAccessibilityStateChanged(boolean enabled); + } + final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { public void setEnabled(boolean enabled) { mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); @@ -78,9 +96,8 @@ public final class AccessibilityManager { public void handleMessage(Message message) { switch (message.what) { case DO_SET_ENABLED : - synchronized (mHandler) { - mIsEnabled = (message.arg1 == 1); - } + final boolean isEnabled = (message.arg1 == 1); + setAccessibilityState(isEnabled); return; default : Log.w(LOG_TAG, "Unknown message type: " + message.what); @@ -117,7 +134,8 @@ public final class AccessibilityManager { mService = service; try { - mIsEnabled = mService.addClient(mClient); + final boolean isEnabled = mService.addClient(mClient); + setAccessibilityState(isEnabled); } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); } @@ -253,4 +271,81 @@ public final class AccessibilityManager { } return Collections.unmodifiableList(services); } + + /** + * Registers an {@link AccessibilityStateChangeListener}. + * + * @param listener The listener. + * @return True if successfully registered. + */ + public boolean addAccessibilityStateChangeListener( + AccessibilityStateChangeListener listener) { + return mAccessibilityStateChangeListeners.add(listener); + } + + /** + * Unregisters an {@link AccessibilityStateChangeListener}. + * + * @param listener The listener. + * @return True if successfully unregistered. + */ + public boolean removeAccessibilityStateChangeListener( + AccessibilityStateChangeListener listener) { + return mAccessibilityStateChangeListeners.remove(listener); + } + + /** + * Sets the enabled state. + * + * @param isEnabled The accessibility state. + */ + private void setAccessibilityState(boolean isEnabled) { + synchronized (mHandler) { + if (isEnabled != mIsEnabled) { + mIsEnabled = isEnabled; + notifyAccessibilityStateChanged(); + } + } + } + + /** + * Notifies the registered {@link AccessibilityStateChangeListener}s. + */ + private void notifyAccessibilityStateChanged() { + final int listenerCount = mAccessibilityStateChangeListeners.size(); + for (int i = 0; i < listenerCount; i++) { + mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled); + } + } + + /** + * Adds an accessibility interaction connection interface for a given window. + * @param windowToken The window token to which a connection is added. + * @param connection The connection. + * + * @hide + */ + public int addAccessibilityInteractionConnection(IWindow windowToken, + IAccessibilityInteractionConnection connection) { + try { + return mService.addAccessibilityInteractionConnection(windowToken, connection); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); + } + return View.NO_ID; + } + + /** + * Removed an accessibility interaction connection interface for a given window. + * @param windowToken The window token to which a connection is removed. + * + * @hide + */ + public void removeAccessibilityInteractionConnection(IWindow windowToken) { + try { + mService.removeAccessibilityInteractionConnection(windowToken); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl new file mode 100644 index 0000000..59175ce --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, 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.view.accessibility; + +parcelable AccessibilityNodeInfo; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java new file mode 100644 index 0000000..752f864 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -0,0 +1,947 @@ +/* + * Copyright (C) 2011 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.view.accessibility; + +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.view.View; + +/** + * This class represents a node of the screen content. From the point of + * view of an accessibility service the screen content is presented as tree + * of accessibility nodes. + * + * TODO(svertoslavganov): Update the documentation, add sample, and describe + * the security policy. + */ +public class AccessibilityNodeInfo implements Parcelable { + + private static final boolean DEBUG = false; + + // Actions. + + /** + * Action that focuses the node. + */ + public static final int ACTION_FOCUS = 0x00000001; + + /** + * Action that unfocuses the node. + */ + public static final int ACTION_CLEAR_FOCUS = 0x00000002; + + /** + * Action that selects the node. + */ + public static final int ACTION_SELECT = 0x00000004; + + /** + * Action that unselects the node. + */ + public static final int ACTION_CLEAR_SELECTION = 0x00000008; + + // Boolean attributes. + + private static final int PROPERTY_CHECKABLE = 0x00000001; + + private static final int PROPERTY_CHECKED = 0x00000002; + + private static final int PROPERTY_FOCUSABLE = 0x00000004; + + private static final int PROPERTY_FOCUSED = 0x00000008; + + private static final int PROPERTY_SELECTED = 0x00000010; + + private static final int PROPERTY_CLICKABLE = 0x00000020; + + private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; + + private static final int PROPERTY_ENABLED = 0x00000080; + + private static final int PROPERTY_PASSWORD = 0x00000100; + + // Readable representations - lazily initialized. + private static SparseArray<String> sActionSymbolicNames; + + // Housekeeping. + private static final int MAX_POOL_SIZE = 50; + private static final Object sPoolLock = new Object(); + private static AccessibilityNodeInfo sPool; + private static int sPoolSize; + private AccessibilityNodeInfo mNext; + private boolean mIsInPool; + private boolean mSealed; + + // Data. + private int mAccessibilityViewId; + private int mAccessibilityWindowId; + private int mParentAccessibilityViewId; + private int mBooleanProperties; + private final Rect mBounds = new Rect(); + + private CharSequence mPackageName; + private CharSequence mClassName; + private CharSequence mText; + private CharSequence mContentDescription; + + private final SparseIntArray mChildAccessibilityIds = new SparseIntArray(); + private int mActions; + + private IAccessibilityServiceConnection mConnection; + + /** + * Hide constructor from clients. + */ + private AccessibilityNodeInfo() { + /* do nothing */ + } + + /** + * Sets the source. + * + * @param source The info source. + */ + public void setSource(View source) { + enforceNotSealed(); + mAccessibilityViewId = source.getAccessibilityViewId(); + mAccessibilityWindowId = source.getAccessibilityWindowId(); + } + + /** + * Gets the id of the window from which the info comes from. + * + * @return The window id. + */ + public int getAccessibilityWindowId() { + return mAccessibilityWindowId; + } + + /** + * Gets the number of children. + * + * @return The child count. + */ + public int getChildCount() { + return mChildAccessibilityIds.size(); + } + + /** + * Get the child at given index. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * @param index The child index. + * @return The child node. + * + * @throws IllegalStateException If called outside of an AccessibilityService. + * + */ + public AccessibilityNodeInfo getChild(int index) { + enforceSealed(); + final int childAccessibilityViewId = mChildAccessibilityIds.get(index); + try { + return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, + childAccessibilityViewId); + } catch (RemoteException e) { + return null; + } + } + + /** + * Adds a child. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param child The child. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void addChild(View child) { + enforceNotSealed(); + final int childAccessibilityViewId = child.getAccessibilityViewId(); + final int index = mChildAccessibilityIds.size(); + mChildAccessibilityIds.put(index, childAccessibilityViewId); + } + + /** + * Gets the actions that can be performed on the node. + * + * @return The bit mask of with actions. + * + * @see AccessibilityNodeInfo#ACTION_FOCUS + * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS + * @see AccessibilityNodeInfo#ACTION_SELECT + * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION + */ + public int getActions() { + return mActions; + } + + /** + * Adds an action that can be performed on the node. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param action The action. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void addAction(int action) { + enforceNotSealed(); + mActions |= action; + } + + /** + * Performs an action on the node. + * <p> + * Note: An action can be performed only if the request is made + * from an {@link android.accessibilityservice.AccessibilityService}. + * </p> + * @param action The action to perform. + * @return True if the action was performed. + * + * @throws IllegalStateException If called outside of an AccessibilityService. + */ + public boolean performAction(int action) { + enforceSealed(); + try { + return mConnection.performAccessibilityAction(mAccessibilityWindowId, + mAccessibilityViewId, action); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the unique id identifying this node's parent. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * @return The node's patent id. + */ + public AccessibilityNodeInfo getParent() { + enforceSealed(); + try { + return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, + mParentAccessibilityViewId); + } catch (RemoteException e) { + return null; + } + } + + /** + * Sets the parent. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param parent The parent. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setParent(View parent) { + enforceNotSealed(); + mParentAccessibilityViewId = parent.getAccessibilityViewId(); + } + + /** + * Gets the node bounds in parent coordinates. + * + * @param outBounds The output node bounds. + */ + public void getBounds(Rect outBounds) { + outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom); + } + + /** + * Sets the node bounds in parent coordinates. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param bounds The node bounds. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setBounds(Rect bounds) { + enforceNotSealed(); + mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Gets whether this node is checkable. + * + * @return True if the node is checkable. + */ + public boolean isCheckable() { + return getBooleanProperty(PROPERTY_CHECKABLE); + } + + /** + * Sets whether this node is checkable. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param checkable True if the node is checkable. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setCheckable(boolean checkable) { + setBooleanProperty(PROPERTY_CHECKABLE, checkable); + } + + /** + * Gets whether this node is checked. + * + * @return True if the node is checked. + */ + public boolean isChecked() { + return getBooleanProperty(PROPERTY_CHECKED); + } + + /** + * Sets whether this node is checked. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param checked True if the node is checked. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setChecked(boolean checked) { + setBooleanProperty(PROPERTY_CHECKED, checked); + } + + /** + * Gets whether this node is focusable. + * + * @return True if the node is focusable. + */ + public boolean isFocusable() { + return getBooleanProperty(PROPERTY_FOCUSABLE); + } + + /** + * Sets whether this node is focusable. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param focusable True if the node is focusable. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFocusable(boolean focusable) { + setBooleanProperty(PROPERTY_FOCUSABLE, focusable); + } + + /** + * Gets whether this node is focused. + * + * @return True if the node is focused. + */ + public boolean isFocused() { + return getBooleanProperty(PROPERTY_FOCUSED); + } + + /** + * Sets whether this node is focused. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param focused True if the node is focused. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFocused(boolean focused) { + setBooleanProperty(PROPERTY_FOCUSED, focused); + } + + /** + * Gets whether this node is selected. + * + * @return True if the node is selected. + */ + public boolean isSelected() { + return getBooleanProperty(PROPERTY_SELECTED); + } + + /** + * Sets whether this node is selected. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param selected True if the node is selected. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setSelected(boolean selected) { + setBooleanProperty(PROPERTY_SELECTED, selected); + } + + /** + * Gets whether this node is clickable. + * + * @return True if the node is clickable. + */ + public boolean isClickable() { + return getBooleanProperty(PROPERTY_CLICKABLE); + } + + /** + * Sets whether this node is clickable. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param clickable True if the node is clickable. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setClickable(boolean clickable) { + setBooleanProperty(PROPERTY_CLICKABLE, clickable); + } + + /** + * Gets whether this node is long clickable. + * + * @return True if the node is long clickable. + */ + public boolean isLongClickable() { + return getBooleanProperty(PROPERTY_LONG_CLICKABLE); + } + + /** + * Sets whether this node is long clickable. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param longClickable True if the node is long clickable. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setLongClickable(boolean longClickable) { + setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); + } + + /** + * Gets whether this node is enabled. + * + * @return True if the node is enabled. + */ + public boolean isEnabled() { + return getBooleanProperty(PROPERTY_ENABLED); + } + + /** + * Sets whether this node is enabled. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param enabled True if the node is enabled. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setEnabled(boolean enabled) { + setBooleanProperty(PROPERTY_ENABLED, enabled); + } + + /** + * Gets whether this node is a password. + * + * @return True if the node is a password. + */ + public boolean isPassword() { + return getBooleanProperty(PROPERTY_PASSWORD); + } + + /** + * Sets whether this node is a password. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param password True if the node is a password. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setPassword(boolean password) { + setBooleanProperty(PROPERTY_PASSWORD, password); + } + + /** + * Gets the package this node comes from. + * + * @return The package name. + */ + public CharSequence getPackageName() { + return mPackageName; + } + + /** + * Sets the package this node comes from. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param packageName The package name. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setPackageName(CharSequence packageName) { + enforceNotSealed(); + mPackageName = packageName; + } + + /** + * Gets the class this node comes from. + * + * @return The class name. + */ + public CharSequence getClassName() { + return mClassName; + } + + /** + * Sets the class this node comes from. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param className The class name. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setClassName(CharSequence className) { + enforceNotSealed(); + mClassName = className; + } + + /** + * Gets the text of this node. + * + * @return The text. + */ + public CharSequence getText() { + return mText; + } + + /** + * Sets the text of this node. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param text The text. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setText(CharSequence text) { + enforceNotSealed(); + mText = text; + } + + /** + * Gets the content description of this node. + * + * @return The content description. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the content description of this node. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param contentDescription The content description. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setContentDescription(CharSequence contentDescription) { + enforceNotSealed(); + mContentDescription = contentDescription; + } + + /** + * Gets the value of a boolean property. + * + * @param property The property. + * @return The value. + */ + private boolean getBooleanProperty(int property) { + return (mBooleanProperties & property) != 0; + } + + /** + * Sets a boolean property. + * + * @param property The property. + * @param value The value. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + private void setBooleanProperty(int property, boolean value) { + enforceNotSealed(); + if (value) { + mBooleanProperties |= property; + } else { + mBooleanProperties &= ~property; + } + } + + /** + * Sets the connection for interacting with the system. + * + * @param connection The client token. + * + * @hide + */ + public final void setConnection(IAccessibilityServiceConnection connection) { + mConnection = connection; + } + + /** + * {@inheritDoc} + */ + public int describeContents() { + return 0; + } + + /** + * Sets if this instance is sealed. + * + * @param sealed Whether is sealed. + * + * @hide + */ + public void setSealed(boolean sealed) { + mSealed = sealed; + } + + /** + * Gets if this instance is sealed. + * + * @return Whether is sealed. + * + * @hide + */ + public boolean isSealed() { + return mSealed; + } + + /** + * Enforces that this instance is sealed. + * + * @throws IllegalStateException If this instance is not sealed. + * + * @hide + */ + protected void enforceSealed() { + if (!isSealed()) { + throw new IllegalStateException("Cannot perform this " + + "action on a not sealed instance."); + } + } + + /** + * Enforces that this instance is not sealed. + * + * @throws IllegalStateException If this instance is sealed. + * + * @hide + */ + protected void enforceNotSealed() { + if (isSealed()) { + throw new IllegalStateException("Cannot perform this " + + "action on an sealed instance."); + } + } + + /** + * Returns a cached instance if such is available otherwise a new one + * and sets the source. + * + * @return An instance. + * + * @see #setSource(View) + */ + public static AccessibilityNodeInfo obtain(View source) { + AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); + info.setSource(source); + return info; + } + + /** + * Returns a cached instance if such is available otherwise a new one. + * + * @return An instance. + */ + public static AccessibilityNodeInfo obtain() { + synchronized (sPoolLock) { + if (sPool != null) { + AccessibilityNodeInfo info = sPool; + sPool = sPool.mNext; + sPoolSize--; + info.mNext = null; + info.mIsInPool = false; + } + return new AccessibilityNodeInfo(); + } + } + + /** + * Return an instance back to be reused. + * <p> + * <b>Note: You must not touch the object after calling this function.</b> + * + * @throws IllegalStateException If the info is already recycled. + */ + public void recycle() { + if (mIsInPool) { + throw new IllegalStateException("Info already recycled!"); + } + clear(); + synchronized (sPoolLock) { + if (sPoolSize <= MAX_POOL_SIZE) { + mNext = sPool; + sPool = this; + mIsInPool = true; + sPoolSize++; + } + } + } + + /** + * {@inheritDoc} + * <p> + * <b>Note: After the instance is written to a parcel it is recycled. + * You must not touch the object after calling this function.</b> + * </p> + */ + public void writeToParcel(Parcel parcel, int flags) { + if (mConnection == null) { + parcel.writeInt(0); + } else { + parcel.writeInt(1); + parcel.writeStrongBinder(mConnection.asBinder()); + } + parcel.writeInt(isSealed() ? 1 : 0); + parcel.writeInt(mAccessibilityViewId); + parcel.writeInt(mAccessibilityWindowId); + parcel.writeInt(mParentAccessibilityViewId); + + SparseIntArray childIds = mChildAccessibilityIds; + final int childIdsSize = childIds.size(); + parcel.writeInt(childIdsSize); + for (int i = 0; i < childIdsSize; i++) { + parcel.writeInt(childIds.valueAt(i)); + } + + parcel.writeInt(mBounds.top); + parcel.writeInt(mBounds.bottom); + parcel.writeInt(mBounds.left); + parcel.writeInt(mBounds.right); + + parcel.writeInt(mActions); + + parcel.writeInt(mBooleanProperties); + + TextUtils.writeToParcel(mPackageName, parcel, flags); + TextUtils.writeToParcel(mClassName, parcel, flags); + TextUtils.writeToParcel(mText, parcel, flags); + TextUtils.writeToParcel(mContentDescription, parcel, flags); + + // Since instances of this class are fetched via synchronous i.e. blocking + // calls in IPCs and we always recycle as soon as the instance is marshaled. + recycle(); + } + + /** + * Creates a new instance from a {@link Parcel}. + * + * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. + */ + private void initFromParcel(Parcel parcel) { + if (parcel.readInt() == 1) { + mConnection = IAccessibilityServiceConnection.Stub.asInterface( + parcel.readStrongBinder()); + } + mSealed = (parcel.readInt() == 1); + mAccessibilityViewId = parcel.readInt(); + mAccessibilityWindowId = parcel.readInt(); + mParentAccessibilityViewId = parcel.readInt(); + + SparseIntArray childIds = mChildAccessibilityIds; + final int childrenSize = parcel.readInt(); + for (int i = 0; i < childrenSize; i++) { + final int childId = parcel.readInt(); + childIds.put(i, childId); + } + + mBounds.top = parcel.readInt(); + mBounds.bottom = parcel.readInt(); + mBounds.left = parcel.readInt(); + mBounds.right = parcel.readInt(); + + mActions = parcel.readInt(); + + mBooleanProperties = parcel.readInt(); + + mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + } + + /** + * Clears the state of this instance. + */ + private void clear() { + mSealed = false; + mConnection = null; + mAccessibilityViewId = View.NO_ID; + mParentAccessibilityViewId = View.NO_ID; + mChildAccessibilityIds.clear(); + mBounds.set(0, 0, 0, 0); + mBooleanProperties = 0; + mPackageName = null; + mClassName = null; + mText = null; + mContentDescription = null; + mActions = 0; + } + + /** + * Gets the human readable action symbolic name. + * + * @param action The action. + * @return The symbolic name. + */ + private static String getActionSymbolicName(int action) { + SparseArray<String> actionSymbolicNames = sActionSymbolicNames; + if (actionSymbolicNames == null) { + actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>(); + actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS"); + actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS"); + actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT"); + actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT"); + } + return actionSymbolicNames.get(action); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mAccessibilityViewId; + result = prime * result + mAccessibilityWindowId; + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(super.toString()); + + if (DEBUG) { + builder.append("; accessibilityId: " + mAccessibilityViewId); + builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId); + SparseIntArray childIds = mChildAccessibilityIds; + builder.append("; childAccessibilityIds: ["); + for (int i = 0, count = childIds.size(); i < count; i++) { + builder.append(childIds.valueAt(i)); + if (i < count - 1) { + builder.append(", "); + } + } + builder.append("]"); + } + + builder.append("; bounds: " + mBounds); + + builder.append("; packageName: ").append(mPackageName); + builder.append("; className: ").append(mClassName); + builder.append("; text: ").append(mText); + builder.append("; contentDescription: ").append(mContentDescription); + + builder.append("; checkable: ").append(isCheckable()); + builder.append("; checked: ").append(isChecked()); + builder.append("; focusable: ").append(isFocusable()); + builder.append("; focused: ").append(isFocused()); + builder.append("; selected: ").append(isSelected()); + builder.append("; clickable: ").append(isClickable()); + builder.append("; longClickable: ").append(isLongClickable()); + builder.append("; enabled: ").append(isEnabled()); + builder.append("; password: ").append(isPassword()); + + builder.append("; ["); + + for (int actionBits = mActions; actionBits != 0;) { + final int action = 1 << Integer.numberOfTrailingZeros(actionBits); + actionBits &= ~action; + builder.append(getActionSymbolicName(action)); + if (actionBits != 0) { + builder.append(", "); + } + } + + builder.append("]"); + + return builder.toString(); + } + + /** + * @see Parcelable.Creator + */ + public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = + new Parcelable.Creator<AccessibilityNodeInfo>() { + public AccessibilityNodeInfo createFromParcel(Parcel parcel) { + AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); + info.initFromParcel(parcel); + return info; + } + + public AccessibilityNodeInfo[] newArray(int size) { + return new AccessibilityNodeInfo[size]; + } + }; +} diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index 7819b17..4bf03a7 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -38,13 +38,14 @@ public class AccessibilityRecord { private static final int PROPERTY_PASSWORD = 0x00000004; private static final int PROPERTY_FULL_SCREEN = 0x00000080; + // Housekeeping private static final int MAX_POOL_SIZE = 10; private static final Object sPoolLock = new Object(); private static AccessibilityRecord sPool; private static int sPoolSize; - private AccessibilityRecord mNext; private boolean mIsInPool; + private boolean mSealed; protected int mBooleanProperties; protected int mCurrentItemIndex; @@ -68,6 +69,26 @@ public class AccessibilityRecord { } /** + * Initialize this record from another one. + * + * @param record The to initialize from. + */ + void init(AccessibilityRecord record) { + mSealed = record.isSealed(); + mBooleanProperties = record.mBooleanProperties; + mCurrentItemIndex = record.mCurrentItemIndex; + mItemCount = record.mItemCount; + mFromIndex = record.mFromIndex; + mAddedCount = record.mAddedCount; + mRemovedCount = record.mRemovedCount; + mClassName = record.mClassName; + mContentDescription = record.mContentDescription; + mBeforeText = record.mBeforeText; + mParcelableData = record.mParcelableData; + mText.addAll(record.mText); + } + + /** * Gets if the source is checked. * * @return True if the view is checked, false otherwise. @@ -80,8 +101,11 @@ public class AccessibilityRecord { * Sets if the source is checked. * * @param isChecked True if the view is checked, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setChecked(boolean isChecked) { + enforceNotSealed(); setBooleanProperty(PROPERTY_CHECKED, isChecked); } @@ -98,8 +122,11 @@ public class AccessibilityRecord { * Sets if the source is enabled. * * @param isEnabled True if the view is enabled, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setEnabled(boolean isEnabled) { + enforceNotSealed(); setBooleanProperty(PROPERTY_ENABLED, isEnabled); } @@ -116,27 +143,33 @@ public class AccessibilityRecord { * Sets if the source is a password field. * * @param isPassword True if the view is a password field, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setPassword(boolean isPassword) { + enforceNotSealed(); setBooleanProperty(PROPERTY_PASSWORD, isPassword); } /** - * Sets if the source is taking the entire screen. + * Gets if the source is taking the entire screen. * - * @param isFullScreen True if the source is full screen, false otherwise. + * @return True if the source is full screen, false otherwise. */ - public void setFullScreen(boolean isFullScreen) { - setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); + public boolean isFullScreen() { + return getBooleanProperty(PROPERTY_FULL_SCREEN); } /** - * Gets if the source is taking the entire screen. + * Sets if the source is taking the entire screen. * - * @return True if the source is full screen, false otherwise. + * @param isFullScreen True if the source is full screen, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. */ - public boolean isFullScreen() { - return getBooleanProperty(PROPERTY_FULL_SCREEN); + public void setFullScreen(boolean isFullScreen) { + enforceNotSealed(); + setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); } /** @@ -152,8 +185,11 @@ public class AccessibilityRecord { * Sets the number of items that can be visited. * * @param itemCount The number of items. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setItemCount(int itemCount) { + enforceNotSealed(); mItemCount = itemCount; } @@ -170,8 +206,11 @@ public class AccessibilityRecord { * Sets the index of the source in the list of items that can be visited. * * @param currentItemIndex The current item index. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setCurrentItemIndex(int currentItemIndex) { + enforceNotSealed(); mCurrentItemIndex = currentItemIndex; } @@ -188,8 +227,11 @@ public class AccessibilityRecord { * Sets the index of the first character of the changed sequence. * * @param fromIndex The index of the first character. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setFromIndex(int fromIndex) { + enforceNotSealed(); mFromIndex = fromIndex; } @@ -206,8 +248,11 @@ public class AccessibilityRecord { * Sets the number of added characters. * * @param addedCount The number of added characters. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setAddedCount(int addedCount) { + enforceNotSealed(); mAddedCount = addedCount; } @@ -224,8 +269,11 @@ public class AccessibilityRecord { * Sets the number of removed characters. * * @param removedCount The number of removed characters. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setRemovedCount(int removedCount) { + enforceNotSealed(); mRemovedCount = removedCount; } @@ -242,8 +290,11 @@ public class AccessibilityRecord { * Sets the class name of the source. * * @param className The lass name. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setClassName(CharSequence className) { + enforceNotSealed(); mClassName = className; } @@ -270,8 +321,11 @@ public class AccessibilityRecord { * Sets the text before a change. * * @param beforeText The text before the change. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setBeforeText(CharSequence beforeText) { + enforceNotSealed(); mBeforeText = beforeText; } @@ -288,8 +342,11 @@ public class AccessibilityRecord { * Sets the description of the source. * * @param contentDescription The description. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setContentDescription(CharSequence contentDescription) { + enforceNotSealed(); mContentDescription = contentDescription; } @@ -306,18 +363,71 @@ public class AccessibilityRecord { * Sets the {@link Parcelable} data of the event. * * @param parcelableData The parcelable data. + * + * @throws IllegalStateException If called from an AccessibilityService. */ public void setParcelableData(Parcelable parcelableData) { + enforceNotSealed(); mParcelableData = parcelableData; } /** + * Sets if this instance is sealed. + * + * @param sealed Whether is sealed. + * + * @hide + */ + public void setSealed(boolean sealed) { + mSealed = sealed; + } + + /** + * Gets if this instance is sealed. + * + * @return Whether is sealed. + * + * @hide + */ + public boolean isSealed() { + return mSealed; + } + + /** + * Enforces that this instance is sealed. + * + * @throws IllegalStateException If this instance is not sealed. + * + * @hide + */ + protected void enforceSealed() { + if (!isSealed()) { + throw new IllegalStateException("Cannot perform this " + + "action on a not sealed instance."); + } + } + + /** + * Enforces that this instance is not sealed. + * + * @throws IllegalStateException If this instance is sealed. + * + * @hide + */ + protected void enforceNotSealed() { + if (isSealed()) { + throw new IllegalStateException("Cannot perform this " + + "action on an sealed instance."); + } + } + + /** * Gets the value of a boolean property. * * @param property The property. * @return The value. */ - public boolean getBooleanProperty(int property) { + private boolean getBooleanProperty(int property) { return (mBooleanProperties & property) == property; } @@ -337,6 +447,19 @@ public class AccessibilityRecord { /** * Returns a cached instance if such is available or a new one is + * instantiated. The instance is initialized with data from the + * given record. + * + * @return An instance. + */ + public static AccessibilityRecord obtain(AccessibilityRecord record) { + AccessibilityRecord clone = AccessibilityRecord.obtain(); + clone.init(record); + return clone; + } + + /** + * Returns a cached instance if such is available or a new one is * instantiated. * * @return An instance. @@ -379,8 +502,11 @@ public class AccessibilityRecord { /** * Clears the state of this instance. + * + * @hide */ protected void clear() { + mSealed = false; mBooleanProperties = 0; mCurrentItemIndex = INVALID_POSITION; mItemCount = 0; diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl new file mode 100644 index 0000000..77dcd07 --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 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.view.accessibility; + +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityInteractionConnectionCallback; + +/** + * Interface for interaction between the AccessibilityManagerService + * and the ViewRoot in a given window. + * + * @hide + */ +oneway interface IAccessibilityInteractionConnection { + + void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback); + + void findAccessibilityNodeInfoByViewId(int id, int interactionId, + IAccessibilityInteractionConnectionCallback callback); + + void findAccessibilityNodeInfosByViewText(String text, int interactionId, + IAccessibilityInteractionConnectionCallback callback); + + void performAccessibilityAction(int accessibilityId, int action, int interactionId, + IAccessibilityInteractionConnectionCallback callback); +} diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl new file mode 100644 index 0000000..9c5e8dc --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 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.view.accessibility; + +import android.view.accessibility.AccessibilityNodeInfo; +import java.util.List; + +/** + * Callback for specifying the result for an asynchronous request made + * via calling a method on IAccessibilityInteractionConnectionCallback. + * + * @hide + */ +oneway interface IAccessibilityInteractionConnectionCallback { + + void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId); + + void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos, + int interactionId); + + void setPerformAccessibilityActionResult(boolean succeeded, int interactionId); +} diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 6b2aae2..b14f02a 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -18,8 +18,13 @@ package android.view.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.accessibilityservice.IEventListener; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityManagerClient; +import android.view.IWindow; /** * Interface implemented by the AccessibilityManagerService called by @@ -38,4 +43,11 @@ interface IAccessibilityManager { List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType); void interrupt(); + + int addAccessibilityInteractionConnection(IWindow windowToken, + in IAccessibilityInteractionConnection connection); + + void removeAccessibilityInteractionConnection(IWindow windowToken); + + IAccessibilityServiceConnection registerEventListener(IEventListener client); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 9e61ecf..a68ca60 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6503,6 +6503,8 @@ public class WebView extends AbsoluteLayout // arrow key events, not trackball events, from one child to the next private boolean mMapTrackballToArrowKeys = true; + private DrawData mDelaySetPicture; + public void setMapTrackballToArrowKeys(boolean setMap) { checkThread(); mMapTrackballToArrowKeys = setMap; @@ -7912,7 +7914,8 @@ public class WebView extends AbsoluteLayout // after WebView's destroy() is called, skip handling messages. return; } - if (mBlockWebkitViewMessages) { + if (mBlockWebkitViewMessages + && msg.what != WEBCORE_INITIALIZED_MSG_ID) { // Blocking messages from webkit return; } @@ -8060,6 +8063,10 @@ public class WebView extends AbsoluteLayout BrowserFrame.DRAWABLEDIR, mContext); AssetManager am = mContext.getAssets(); nativeCreate(msg.arg1, drawableDir, am); + if (mDelaySetPicture != null) { + setNewPicture(mDelaySetPicture); + mDelaySetPicture = null; + } break; case UPDATE_TEXTFIELD_TEXT_MSG_ID: // Make sure that the textfield is currently focused @@ -8371,6 +8378,15 @@ public class WebView extends AbsoluteLayout } void setNewPicture(final WebViewCore.DrawData draw) { + if (mNativeClass == 0) { + if (mDelaySetPicture != null) { + throw new IllegalStateException( + "Tried to setNewPicture with a delay picture already set! (memory leak)"); + } + // Not initialized yet, delay set + mDelaySetPicture = draw; + return; + } WebViewCore.ViewState viewState = draw.mViewState; boolean isPictureAfterFirstLayout = viewState != null; setBaseLayer(draw.mBaseLayer, draw.mInvalRegion, diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 4ee16e7..6b498fe 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -115,7 +115,7 @@ public class FrameLayout extends ViewGroup { } /** - * Describes how the foreground is positioned. Defaults to BEFORE and TOP. + * Describes how the foreground is positioned. Defaults to START and TOP. * * @param foregroundGravity See {@link android.view.Gravity} * @@ -125,7 +125,7 @@ public class FrameLayout extends ViewGroup { public void setForegroundGravity(int foregroundGravity) { if (mForegroundGravity != foregroundGravity) { if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { - foregroundGravity |= Gravity.BEFORE; + foregroundGravity |= Gravity.START; } if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index f843574..3876735 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -116,10 +116,10 @@ public class LinearLayout extends ViewGroup { equals = Gravity.LEFT, name = "LEFT"), @ViewDebug.FlagToString(mask = Gravity.RIGHT, equals = Gravity.RIGHT, name = "RIGHT"), - @ViewDebug.FlagToString(mask = Gravity.BEFORE, - equals = Gravity.BEFORE, name = "BEFORE"), - @ViewDebug.FlagToString(mask = Gravity.AFTER, - equals = Gravity.AFTER, name = "AFTER"), + @ViewDebug.FlagToString(mask = Gravity.START, + equals = Gravity.START, name = "START"), + @ViewDebug.FlagToString(mask = Gravity.END, + equals = Gravity.END, name = "END"), @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL, equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"), @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL, @@ -135,7 +135,7 @@ public class LinearLayout extends ViewGroup { @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION, equals = Gravity.RELATIVE_HORIZONTAL_DIRECTION, name = "RELATIVE") }) - private int mGravity = Gravity.BEFORE | Gravity.TOP; + private int mGravity = Gravity.START | Gravity.TOP; @ViewDebug.ExportedProperty(category = "measurement") private int mTotalLength; @@ -1649,7 +1649,7 @@ public class LinearLayout extends ViewGroup { public void setGravity(int gravity) { if (mGravity != gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.BEFORE; + gravity |= Gravity.START; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { @@ -1742,8 +1742,8 @@ public class LinearLayout extends ViewGroup { @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), - @ViewDebug.IntToString(from = Gravity.BEFORE, to = "BEFORE"), - @ViewDebug.IntToString(from = Gravity.AFTER, to = "AFTER"), + @ViewDebug.IntToString(from = Gravity.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index acd8539..a4771d5 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,6 +16,15 @@ package android.widget; +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.SortedSet; +import java.util.TreeSet; + import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -32,14 +41,6 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; -import com.android.internal.R; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.SortedSet; -import java.util.TreeSet; import static android.util.Log.d; @@ -222,7 +223,7 @@ public class RelativeLayout extends ViewGroup { public void setGravity(int gravity) { if (mGravity != gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.BEFORE; + gravity |= Gravity.START; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { @@ -1440,6 +1441,7 @@ public class RelativeLayout extends ViewGroup { ); private Node mNext; + private boolean mIsPooled; public void setNextPoolable(Node element) { mNext = element; @@ -1449,6 +1451,14 @@ public class RelativeLayout extends ViewGroup { return mNext; } + public boolean isPooled() { + return mIsPooled; + } + + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } + static Node acquire(View view) { final Node node = sPool.acquire(); node.view = view; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 35e78fb..eba9d37 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -113,6 +113,7 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; @@ -2094,7 +2095,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public void setGravity(int gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.BEFORE; + gravity |= Gravity.START; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { gravity |= Gravity.TOP; @@ -7647,7 +7648,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected int computeVerticalScrollExtent() { return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom(); } - + + @Override + public void findViewsWithText(ArrayList<View> outViews, CharSequence text) { + CharSequence thisText = getText(); + if (TextUtils.isEmpty(thisText)) { + return; + } + if (thisText.toString().toLowerCase().contains(text)) { + outViews.add(this); + } + } + public enum BufferType { NORMAL, SPANNABLE, EDITABLE, } @@ -7899,6 +7911,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener event.setPassword(isPassword); } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + + final boolean isPassword = hasPasswordTransformationMethod(); + if (!isPassword) { + info.setText(getText()); + } + info.setPassword(isPassword); + } + void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int removedCount, int addedCount) { AccessibilityEvent event = @@ -10005,7 +10028,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mSelectAllOnFocus = false; - private int mGravity = Gravity.TOP | Gravity.BEFORE; + private int mGravity = Gravity.TOP | Gravity.START; private boolean mHorizontallyScrolling; private int mAutoLinkMask; diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index c41b2cb..b9948fe 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -16,8 +16,6 @@ package com.android.internal.view; -import android.content.ClipData; -import android.content.ClipDescription; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; @@ -26,8 +24,6 @@ import android.os.RemoteException; import android.view.DragEvent; import android.view.IWindow; import android.view.IWindowSession; -import android.view.KeyEvent; -import android.view.MotionEvent; public class BaseIWindow extends IWindow.Stub { private IWindowSession mSession; diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 4a09232..942aa8a 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -755,32 +755,46 @@ public: jfloat x, jfloat y, int flags, SkPaint* paint) { jint count = end - start; - sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue( - paint, textArray, start, count, end, flags); + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + value = gTextLayoutCache.getValue(paint, textArray, start, count, end, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); return ; } +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, textArray, start, count, end, flags); +#endif + #if DEBUG_GLYPHS logGlyphs(value); #endif - doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), - x, y, flags, paint); + + doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), + x, y, flags, paint); } static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray, int start, int count, int contextCount, jfloat x, jfloat y, int flags, SkPaint* paint) { - sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue( - paint, textArray, start, count, contextCount, flags); + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + value = gTextLayoutCache.getValue(paint, textArray, start, count, contextCount, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); return ; } +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, textArray, start, count, contextCount, flags); +#endif + #if DEBUG_GLYPHS logGlyphs(value); #endif + doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint); } diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp index 46e6c2b..7e89a37 100644 --- a/core/jni/android/graphics/TextLayout.cpp +++ b/core/jni/android/graphics/TextLayout.cpp @@ -254,21 +254,21 @@ void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars, void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat& resultTotalAdvance) { + sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE // Return advances from the cache. Compute them if needed - sp<TextLayoutCacheValue> layout = gTextLayoutCache.getValue( + value = gTextLayoutCache.getValue( paint, chars, start, count, contextCount, dirFlags); - if (layout != NULL) { +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, chars, start, count, contextCount, dirFlags); +#endif + if (value != NULL) { if (resultAdvances != NULL) { - memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat)); + memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); } - resultTotalAdvance = layout->getTotalAdvance(); + resultTotalAdvance = value->getTotalAdvance(); } -#else - // Compute advances and return them - TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, - dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL ); -#endif } void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index e5c2848..0960b25 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -38,18 +38,7 @@ #include <grp.h> #include <pwd.h> #include <signal.h> - -/* desktop Linux needs a little help with gettid() */ -#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS) -#define __KERNEL__ -# include <linux/unistd.h> -#ifdef _syscall0 -_syscall0(pid_t,gettid) -#else -pid_t gettid() { return syscall(__NR_gettid);} -#endif -#undef __KERNEL__ -#endif +#include <unistd.h> #define POLICY_DEBUG 0 #define GUARD_THREAD_PRIORITY 0 diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 31988f7..57a97bd 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -438,12 +438,17 @@ static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz, static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { #if RTL_USE_HARFBUZZ - sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue( - paint, text, 0, count, count, flags); + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + value = gTextLayoutCache.getValue(paint, text, 0, count, count, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); return ; } +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, text, 0, count, count, flags); +#endif #if DEBUG_GLYPHS logGlyphs(value); #endif @@ -466,12 +471,17 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, jint start, jint count, jint contextCount, jfloat x, jfloat y, int flags, SkPaint* paint) { #if RTL_USE_HARFBUZZ - sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue( - paint, text, start, count, contextCount, flags); + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + value = gTextLayoutCache.getValue(paint, text, start, count, contextCount, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); return ; } +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, text, start, count, contextCount, flags); +#endif #if DEBUG_GLYPHS logGlyphs(value); #endif diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 110268e..3efb83c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -677,6 +677,14 @@ android:label="@string/permlab_dump" android:description="@string/permdesc_dump" /> + <!-- Allows an application to retrieve the content of the active window + An active window is the window that has fired an accessibility event. --> + <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:protectionLevel="signatureOrSystem" + android:label="@string/permlab_retrieve_window_content" + android:description="@string/permdesc_retrieve_window_content" /> + <!-- Allows an application to open windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_SYSTEM_ALERT}, shown on top of all other applications. Very few applications diff --git a/core/res/res/drawable-hdpi/pointer_arrow.png b/core/res/res/drawable-hdpi/pointer_arrow.png Binary files differnew file mode 100644 index 0000000..fbd187c --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_arrow.png diff --git a/core/res/res/drawable-hdpi/pointer_arrow_icon.xml b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml new file mode 100644 index 0000000..2f5676f --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_arrow" + android:hotSpotX="6" + android:hotSpotY="6" /> diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor.png b/core/res/res/drawable-hdpi/pointer_spot_anchor.png Binary files differnew file mode 100644 index 0000000..d7aca36 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_anchor.png diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml new file mode 100644 index 0000000..2222b8e --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_anchor" + android:hotSpotX="33" + android:hotSpotY="33" /> diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover.png b/core/res/res/drawable-hdpi/pointer_spot_hover.png Binary files differnew file mode 100644 index 0000000..5041aa3 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_hover.png diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml new file mode 100644 index 0000000..dc62a69 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_hover" + android:hotSpotX="33" + android:hotSpotY="33" /> diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch.png b/core/res/res/drawable-hdpi/pointer_spot_touch.png Binary files differnew file mode 100644 index 0000000..64a42a1 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_touch.png diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml new file mode 100644 index 0000000..4bffee6 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_touch" + android:hotSpotX="24" + android:hotSpotY="24" /> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 38b62d4..cab43ac 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -412,6 +412,10 @@ <!-- no translation found for permlab_setOrientation (3365947717163866844) --> <skip /> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Laat \'n program toe om die skerm te enige tyd te draai. Behoort vir gewone programme nooit nodig te wees nie."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) --> <skip /> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Laat program toe om te versoek dat die voorsiende materiaal aan alle aanhoudende prosesse gestuur word."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 9c22f0e..b97bc7a 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -396,6 +396,10 @@ <!-- no translation found for permlab_setOrientation (3365947717163866844) --> <skip /> <string name="permdesc_setOrientation" msgid="6335814461615851863">"ትግበራው በማንኛውም ሰዓት የማያንማሽከርከሪያለመለወጥ ይፈቅዳል።ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) --> <skip /> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"ትግበራዎች ለሁሉም ተከታታይ ሂደቶች ልከው የሚያቀርቧቸው ሲግናሎችን ለመጠየቅ ይፈቅዳል።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index c9454d3..8d5ecb8 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"للسماح للمالك بإرسال الأهداف إلى أحد مشرفي الجهاز. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"تغيير اتجاه الشاشة"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"للسماح لتطبيق ما بتغيير تدوير الشاشة في أي وقت. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"إرسال إشارات Linux للتطبيقات"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"للسماح للتطبيق بطلب إرسال الإشارة المزوّدة لجميع العمليات المستمرة."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"تشغيل التطبيق دائمًا"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 70eb79a..b43b50f 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Разрешава на притежателя да изпраща намерения до администратор на устройството. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"промяна на ориентацията на екрана"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Разрешава на приложението да променя ориентацията на екрана по всяко време. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"изпращане на сигнали от Linux до приложенията"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Разрешава на приложението да подаде заявка предоставеният сигнал да се изпрати до всички постоянни процеси."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"задаване на постоянно изпълнение на приложението"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 45201c1..2947b93 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet al titular enviar intencions a un administrador del sistema. No s\'hauria de necessitar mai per a les aplicacions normals."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"canviar l\'orientació de la pantalla"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet a una aplicació canviar el gir de la pantalla en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar senyals Linux a les aplicacions"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet a l\'aplicació sol·licitar que el senyal subministrat s\'enviï a tots els processos persistents."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"fer que l\'aplicació s\'executi sempre"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index f88e381..92deaca 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteli oprávnění odesílat informace správci zařízení. Běžné aplikace by toto oprávnění nikdy neměly požadovat."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"změna orientace obrazovky"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikaci kdykoli změnit orientaci obrazovky. Běžné aplikace by toto nastavení nikdy neměly využívat."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odeslání signálů Linux aplikacím"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikaci vyžádat zaslání poskytnutého signálu všem trvalým procesům."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spuštění aplikace"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index c6946d4..a3149a2 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillader brugeren at sende hensigter til en enhedsadministrator. Bør aldrig være nødvendigt for almindelige programmer."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"skift skærmretning"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillader, at et program ændrer rotationen af skærmen når som helst. Bør aldrig være nødvendigt til normale programmer."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux-signaler til programmer"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillader, at programmet kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"lad altid programmet køre"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index ffef226..b6b6be5 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ermöglicht dem Halter, Intents an einen Geräteadministrator zu senden. Sollte nie für normale Anwendungen benötigt werden."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"Bildschirmausrichtung ändern"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ermöglicht der Anwendung, die Bildschirmdrehung jederzeit zu ändern. Sollte nicht für normale Anwendungen benötigt werden."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-Signale an Anwendungen senden"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ermöglicht der Anwendung, das Senden des gelieferten Signals an alle anhaltenden Prozesse zu fordern"</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"Anwendungen permanent ausführen"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index bf1d432..a5e13f1 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Επιτρέπει στον κάτοχο την αποστολή στόχων σε έναν διαχειριστή συσκευής. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"αλλαγή προσανατολισμού οθόνης"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Επιτρέπει σε μια εφαρμογή την αλλαγή της περιστροφής της οθόνης οποιαδήποτε στιγμή. Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"αποστολή σημάτων Linux σε εφαρμογές"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Επιτρέπει σε μια εφαρμογή την αποστολή αιτήματος για την αποστολή του παρεχόμενου σήματος σε όλες τις υπάρχουσες διαδικασίες."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"η εφαρμογή να εκτελείται συνεχώς"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index b0e77ba..d5ef98b 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Allows the holder to send intents to a device administrator. Should never be needed for normal applications."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"change screen orientation"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Allows an application to change the rotation of the screen at any time. Should never be needed for normal applications."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux signals to applications"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Allows application to request that the supplied signal be sent to all persistent processes."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"make application always run"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index fb97c4a..3cb61e4 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que el propietario envíe sus intentos a un administrador de dispositivos. No se necesita para las aplicaciones normales."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar la orientación de la pantalla"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Admite una aplicación que cambia la rotación de la pantalla en cualquier momento. Se debe evitar utilizarlo en aplicaciones normales."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales de Linux a las aplicaciones"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Admite que la aplicación solicite el envío de la señal suministrada a todos los procesos continuos."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que siempre se ejecute la aplicación"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index a1e3a54..9db80ca 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite enviar intentos a un administrador de dispositivos. Este permiso nunca debería ser necesario para las aplicaciones normales."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar orientación de la pantalla"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesario nunca para las aplicaciones normales."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales Linux a aplicaciones"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que la aplicación solicite que la señal suministrada se envíe a todos los procesos persistentes."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que la aplicación se ejecute siempre"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 00d173d..3fcb21d 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"به نگهدارنده اجازه می دهد مفاد را به یک سرپرست دستگاه ارسال کند. هرگز برای برنامه های معمولی مورد نیاز نیست."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"تغییر جهت صفحه"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"به یک برنامه کاربردی اجازه می دهد چرخش صفحه را در هر زمانی تغییر دهد. هرگز برای برنامه های معمولی مورد نیاز نیست."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ارسال سیگنال های Linux برای برنامه ها"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"به برنامه کاربردی اجازه می دهد درخواست کند که سیگنال ارائه شده را به تمام فرایندهای ثابت ارسال کند."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"برنامه همیشه اجرا شود"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 2d3bf48..70e5ff7 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Antaa sovelluksen lähettää kyselyitä laitteen järjestelmänvalvojalle. Ei tavallisten sovelluksien käyttöön."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"muuta näytön suuntaa"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Antaa sovelluksen vaihtaa näytön suuntaa milloin tahansa. Ei tavallisten sovelluksien käyttöön."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"lähettää sovelluksiin Linux-signaaleja"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Antaa sovelluksen pyytää, että signaali lähetetään kaikille kiinteille prosesseille."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"aseta sovellus olemaan jatkuvasti käynnissä"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 3f3eb93..c618edf 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet à l\'application d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standard ne devraient jamais avoir recours à cette fonctionnalité."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"Changement d\'orientation de l\'écran"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Envoi de signaux Linux aux applications"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet à une application de demander que le signal fourni soit envoyé à tous les processus persistants."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"Exécution de l\'application en continu"</string> @@ -449,7 +453,7 @@ <string name="permlab_nfc" msgid="4423351274757876953">"contrôler la communication en champ proche"</string> <string name="permdesc_nfc" msgid="9171401851954407226">"Permet à une application de communiquer avec des tags, cartes et lecteurs prenant en charge la communication en champ proche (NFC)."</string> <string name="permlab_vpn" msgid="8345800584532175312">"intercepter et modifier l\'ensemble du trafic réseau"</string> - <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et d\'inspecter l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string> + <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et de contrôler l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string> <string name="permlab_disableKeyguard" msgid="4977406164311535092">"Désactivation du verrouillage des touches"</string> <string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet à une application de désactiver le verrouillage des touches et toute sécurité par mot de passe. Exemple : Votre téléphone désactive le verrouillage du clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string> <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Lecture des paramètres de synchronisation"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 773a8f3..f17a272 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Nositelju omogućuje slanje namjera administratoru uređaja. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"promjena orijentacije zaslona"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Aplikaciji omogućuje promjenu rotacije zaslona u svakom trenutku. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"slanje Linux signala u aplikaciju"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Aplikacijama omogućuje traženje zahtjeva da se dobiveni signal pošalje na sve trajne postupke."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"uvijek pokrenuta aplikacija"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 09dd648..0c2aef3 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Lehetővé teszi a használó számára, hogy célokat küldjön egy eszközkezelőnek. A normál alkalmazásoknak erre soha nincs szüksége."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"képernyő irányának módosítása"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lehetővé teszi egy alkalmazás számára a képernyő elforgatásának módosítását. A normál alkalmazásoknak erre soha nincs szüksége."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux jelek küldése alkalmazásoknak"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lehetővé teszi az alkalmazás számára, hogy a megadott jelet elküldje az összes állandó folyamatnak."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"alkalmazások folyamatos futtatása"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b2f3af8..00e2dd4 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Mengizinkan pemegang mengirimkan tujuan kepada administrator perangkat. Tidak diperlukan untuk aplikasi normal."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ubah orientasi layar"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Mengizinkan aplikasi mengubah rotasi layar kapan saja. Tidak diperlukan untuk aplikasi normal."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"kirim sinyal Linux ke aplikasi"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Mengizinkan aplikasi meminta sinyal yang diberikan untuk dikirimkan ke semua proses yang ada."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"jadikan aplikasi selalu berjalan"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 19d317e..0d78861 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Consente l\'invio di intent a un amministratore del dispositivo. L\'autorizzazione non deve mai essere necessaria per le normali applicazioni."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"modifica orientamento dello schermo"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"invio segnali Linuz alle applicazioni"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"esecuzione permanente delle applicazioni"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 4aba678..6b50982 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"מאפשר למשתמש לשלוח כוונות למנהל התקן. לא אמור להידרש לעולם ביישומים רגילים."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"שנה את כיוון המסך"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"מאפשר ליישום לשנות את סיבוב המסך בכל עת. לא אמור להידרש לעולם ביישומים רגילים."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"שלח אותות Linux ליישומים"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"מאפשר ליישום לבקש שהאות המסופק יישלח לכל התהליכים המתמשכים."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"הגדר את היישום לפעול תמיד"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index e078274..bdad7b6 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"デバイス管理者へのintentの送信を所有者に許可します。通常のアプリケーションでは不要です。"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"画面の向きの変更"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"いつでも画面の回転を変更することをアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。"</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linuxのシグナルをアプリケーションに送信"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"受信した電波を継続プロセスに送信することをアプリケーションに許可します。"</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"アプリケーションを常に実行する"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 4d1778e..5a3584a 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"애플리케이션이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"애플리케이션에 Linux 시그널 보내기"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"애플리케이션이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"애플리케이션이 항상 실행되도록 설정"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 5b7ef8c..eab92c6 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Leidžia savininkui siųsti tikslus įrenginio administratoriui. Neturėtų reikėti įprastose programose."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"keisti ekrano padėtį"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Leidžia programai bet kuriuo metu keisti ekrano sukimą. Neturėtų reikėti įprastoms programoms."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"programoms siųsti „Linux“ signalus"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Leidžia programai pateikti užklausą, kad teikiamas signalas būtų siunčiamas visiems nuolatiniams procesams."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"nustatyti, kad programa būtų visada paleista"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 2aa49ca..5c5525c 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ļauj īpašniekam sūtīt nolūkus ierīces administratoram. Nekad nav nepieciešams parastajām lietojumprogrammām."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"mainīt ekrāna orientāciju"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ļauj lietojumprogrammai jebkurā brīdī mainīt ekrāna pagriešanas iestatījumu. Parastajām lietojumprogrammām nekad nav nepieciešama."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sūtīt Linux signālus uz lietojumprogrammām"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ļauj lietojumprogrammai pieprasīt, lai piegādātais signāls tiktu sūtīts uz visiem pastāvīgajiem procesiem."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"likt lietojumprogrammai vienmēr darboties"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index e82fc10..6b0e18e 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -297,6 +297,10 @@ <skip /> <string name="permlab_setOrientation" msgid="3365947717163866844">"tukar orientasi skrin"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Membolehkan aplikasi untuk menukar putaran skrin pada bila-bila masa. Tidak seharusnya diperlukan untuk aplikasi biasa."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"hantar isyarat Linux kepada aplikasi"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Membenarkan aplikasi meminta isyarat yang dibekalkan dihantar kepada semua proses gigih."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"buatkan aplikasi sentiasa berjalan"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 02a414c..2413685 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillater innehaveren å sende hensikter til enhetsadministrator. Bør aldri være nødvendig for normale programmer."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"snu skjermen"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sende Linux-signaler til applikasjoner"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lar applikasjonen spørre om at et gitt signal blir sendt til alle varige prosesser."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"forbli kjørende"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index ad214bf..dbb7e98 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Staat de houder toe intenties te verzenden naar een apparaatbeheerder. Nooit vereist voor normale toepassingen."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"schermstand wijzigen"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Hiermee kan een app op elk gewenst moment de oriëntatie van het scherm wijzigen. Nooit vereist voor normale apps."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-signalen verzenden naar toepassingen"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Hiermee kan de app ervoor zorgen dat het geleverde signaal wordt verzonden naar alle persistente processen."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"app altijd laten uitvoeren"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index ed877bb..36d3cdd 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Zezwala posiadaczowi na wysyłanie informacji o zamiarach do administratora urządzenia. Opcja nie powinna być nigdy potrzebna w przypadku zwykłych aplikacji."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"zmienianie orientacji ekranu"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pozwala aplikacji na zmianę orientacji ekranu w dowolnym momencie. Nigdy nie powinno być potrzeby stosowania w normalnych aplikacjach."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"wysyłanie sygnałów systemu Linux do aplikacji"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pozwala aplikacjom żądać, aby dostarczany sygnał był wysyłany do wszystkich trwających procesów."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"sprawianie, że aplikacja jest cały czas uruchomiona"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 7ba9ad0..9cec4b6 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite ao titular enviar intenções para um administrador do dispositivo. Nunca deverá ser necessário para aplicações normais."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"mudar orientação do ecrã"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite a uma aplicação mudar a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais Linux para aplicações"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite à aplicação pedir que o sinal fornecido seja enviado a todos os processos persistentes."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"fazer com que a aplicação seja sempre executada"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 750a751..57737c9 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que o detentor envie tentativas ao administrador de um dispositivo. Não é necessário para aplicativos normais."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"alterar orientação da tela"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Aplicativos normais não devem precisar disso em momento algum."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais de Linux para os aplicativos"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"executar sempre o aplicativo"</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index ff2eecb..60f6049 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -275,6 +275,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permetta al possessur da trametter intenziuns a l\'administratur dal apparat periferic. Betg previs per applicaziuns normalas."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"midar l\'orientaziun dal visur"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permetta a l\'applicaziun da midar da tut temp la orientaziun dal visur. Betg previs per applicaziuns normalas."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trametter signals Linux a las applicaziuns"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permetta a l\'applicaziun da pretender ch\'il signal furnì vegnia tramess a tut ils process persistents."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"exequir permanentamain applicaziuns"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 7fb4f7318..1961903 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite proprietarului să trimită intenţii către un administrator al dispozitivului. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"modificare orientare ecran"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite unei aplicaţii să modifice rotaţia ecranului în orice moment. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trimitere semnale Linux către aplicaţii"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite aplicaţiei să solicite trimiterea semnalului furnizat către toate procesele persistente."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"rulare întotdeauna a aplicaţiei"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index fe5815f..83dc30f 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Позволяет владельцу отправлять целевые значения администратору устройства. Никогда не используется обычными приложениями."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"изменять ориентацию экрана"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Позволяет приложению изменять ориентацию экрана в любое время. Не требуется для обычных приложений."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"отправлять приложениям сигналы Linux"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Позволяет приложению направлять запрос на передачу предоставленного сигнала всем постоянным процессам."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"запускать постоянную работу приложения"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index d7d9725..3439f59 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteľovi odosielať informácie správcovi zariadenia. Bežné aplikácie by toto oprávnenie nemali nikdy požadovať."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"zmena orientácie obrazovky"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikácii kedykoľvek zmeniť orientáciu obrazovky. Bežné aplikácie by toto nastavenie nemali vôbec využívať."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odoslanie signálov systému Linux aplikáciám"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikácii vyžiadať zaslanie poskytnutého signálu všetkým trvalým procesom."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spustenie aplikácie"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index cbb097b..cd4cd07 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Dovoljuje lastniku, da pošlje namere skrbniku naprave. Nikoli se ne uporablja za navadne programe."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"spreminjanje usmerjenosti zaslona"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Dovoljuje, da program kadar koli spremeni smer sukanja zaslona. Tega nikoli ni treba uporabiti za navadne programe."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"pošiljanje signalov Linuxa programom"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Dovoljuje, da program zahteva, da je posredovan signal poslan vsem trajnim procesom."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"neprekinjeno izvajanje programov"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 93faf2f..59329b4 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Омогућава власнику да шаље своје намере администратору уређаја. Обичне апликације никада не би требало да је користе."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"промена положаја екрана"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Омогућава да апликација у сваком тренутку промени ротацију екрана. Обичне апликације никада не би требало да је користе."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"слање Linux сигнала апликацијама"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Омогућава да апликација захтева да испоручени сигнал буде послат свим трајним процесима."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"омогућавање непрекидне активности апликације"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index c3561d7..a3350e5 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillåter att innehavaren skickar avsikter till en enhetsadministratör. Vanliga program behöver aldrig göra detta."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ändra bildskärmens rikting"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillåter att ett program när som helst ändrar skärmens rotering. Behövs inte för vanliga program."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"skicka Linux-signaler till appar"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillåter att programmet begär att den angivna signalen skickas till alla beständiga processer."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"se till att programmet alltid körs"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 453f74e..ff1907a 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -396,6 +396,10 @@ <!-- no translation found for permlab_setOrientation (3365947717163866844) --> <skip /> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Huruhusu programu kubadilisha uzungukaji wa skrini wakati wowote. Haipaswi kuhitajika kwa programu za kawaida za kompyuta."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) --> <skip /> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Huruhusu programu kuomba kwamba mawimbi yaliyotolewa yatumwe kwa michakato yote isiyokoma."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 54b33f4..be958dc 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"อนุญาตให้ผู้ถือส่งเนื้อหาไปยังโปรแกรมควบคุมอุปกรณ์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"เปลี่ยนการวางแนวหน้าจอ"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"อนุญาตให้แอปพลิเคชันเปลี่ยนการหมุนหน้าจอได้ตลอดเวลา ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ส่งสัญญาณ Linux ไปยังแอปพลิเคชัน"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"อนุญาตให้แอปพลิเคชันร้องขอให้ส่งสัญญาณแจ้งไปยังกระบวนการที่ยังทำงานอยู่ทั้งหมด"</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"ทำให้แอปพลิเคชันทำงานตลอดเวลา"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 9eeac34..b6942a4 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Pinapayagan ang holder na magpadala ng mga intensyon sa administrator ng device. Hindi kailanman dapat kailanganin para sa mga normal na application."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"baguhin ang orientation ng screen"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pinapayagan ang isang application na baguhin ang pag-rotate ng screen anumang oras. Hindi kailanman dapat kailanganin para sa mga normal na application."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"magpadala ng mga Linux signal sa mga application"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pinapayagan ang application na hilinging ipadala ang na-supply na signal sa lahat ng mga paulit-ulit na proseso."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"palaging patakbuhin ang mga application"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 7f1d3c3..e97d834 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cihazın sahibinin cihaz yöneticisine amaç göndermesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ekran yönünü değiştir"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Uygulamaların ekran yönünü istedikleri zaman değiştirmesine izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"uygulamalara Linux sinyalleri gönder"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Uygulamaların, sağlanan sinyalin tüm kalıcı işlemlere gönderilmesini istemesine izin verir."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"uygulamayı her zaman çalıştır"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 00e6333..07b11f7 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Дозволяє власнику надсилати цілі адміністратору пристрою. Ніколи не потрібний для звичайних програм."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"змінювати орієнтацію екрана"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Дозволяє програмі будь-коли змінювати обертання екрана. Ніколи не потрібний для звичайних програм."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"надсилати сигнали Linux програмам"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Дозволяє програмі подавати запит щодо надсилання наданого сигналу всім постійним процесам."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"збер. програму завжди запущ."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 45c3ad8..cfb53ac 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cho phép chủ nhân gửi các ý định đến quản trị viên thiết bị. Không cần thiết cho các ứng dụng thông thường."</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"thay đổi hướng màn hình"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Cho phép ứng dụng thay đổi việc xoay màn hình bất kỳ khi nào. Không cần thiết cho các ứng dụng thông thường."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"gửi tín hiệu Linux đến ứng dụng"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Cho phép ứng dụng yêu cầu tín hiệu đã cung cấp được gửi đến tất cả các quá trình liên tục."</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"đặt ứng dụng luôn chạy"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index f5ee751..b45b346 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允许持有对象将意向发送到设备管理器。普通的应用程序一律无需此权限。"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"向应用程序发送 Linux 信号"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允许应用程序请求将所提供的信号发送给所有持久进程。"</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"让应用程序始终运行"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 1779baf..399a5b0 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -265,6 +265,10 @@ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允許應用程式將調用請求 (intent) 傳送至裝置管理員;一般應用程式不需使用此選項。"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方向"</string> <string name="permdesc_setOrientation" msgid="6335814461615851863">"允許應用程式可隨時變更螢幕旋轉方向。一般應用不應使用這項功能。"</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"傳送 Linux 訊號到應用程式"</string> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允許應用程式要求將支援的訊號傳送到所有持續的程序。"</string> <string name="permlab_persistentActivity" msgid="8659652042401085862">"設定應用程式持續執行"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index ad143a7..6f147e7 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -396,6 +396,10 @@ <!-- no translation found for permlab_setOrientation (3365947717163866844) --> <skip /> <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ivumela uhlelo lokusebenza ukushintsha ukujikeleleza kwesikrini nganoma isiphi isikhathi. Ayisoze yadingeka izinhlelo zokusebenza ezivamile."</string> + <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) --> + <skip /> + <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) --> + <skip /> <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) --> <skip /> <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ivumela uhlelo lokusebenza ukucela ukuthi isiginali enikeziwe ithunyelwe kuzo zonke izinqubo eziphikelelayo"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fc14a2a..f9fa51c 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -156,6 +156,7 @@ <!-- Regex array of allowable upstream ifaces for tethering - for example if you want tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array --> + <!-- Interfaces will be prioritized according to the order listed --> <string-array translatable="false" name="config_tether_upstream_regexs"> </string-array> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8a4b74b..29c23a6 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -562,6 +562,13 @@ never normally need.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_retrieve_window_content">retrieve screen content</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_retrieve_window_content">Allows application to retrieve + the content of the active window. Malicious applications may retrieve + the entire window content and examine all its text except passwords.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_shutdown">partial shutdown</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_shutdown">Puts the activity manager into a shutdown diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 6084dd2..8e2d925 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -98,6 +98,9 @@ <uses-permission android:name="android.permission.ASEC_RENAME" /> <uses-permission android:name="android.permission.SHUTDOWN" /> + <!-- accessibility test permissions --> + <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> + <application android:theme="@style/Theme"> <uses-library android:name="android.test.runner" /> <activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test"> @@ -1225,6 +1228,13 @@ </intent-filter> </activity> + <activity android:name="android.accessibilityservice.InterrogationActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + </application> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml new file mode 100644 index 0000000..28d965b --- /dev/null +++ b/core/tests/coretests/res/layout/interrogation_activity.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, 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. +*/ +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + > + + <LinearLayout + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + <Button + android:id="@+id/button1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button1" + /> + <Button + android:id="@+id/button2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button2" + /> + <Button + android:id="@+id/button3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button3" + /> + </LinearLayout> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + <Button + android:id="@+id/button4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button4" + /> + <Button + android:id="@+id/button5" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button5" + /> + <Button + android:id="@+id/button6" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button6" + /> + </LinearLayout> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + <Button + android:id="@+id/button7" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button7" + /> + <Button + android:id="@+id/button8" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button8" + /> + <Button + android:id="@+id/button9" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button9" + /> + </LinearLayout> + +</LinearLayout> diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml index 807386a..f51b08e 100644 --- a/core/tests/coretests/res/values/strings.xml +++ b/core/tests/coretests/res/values/strings.xml @@ -117,4 +117,16 @@ <string name="searchable_label">SearchManager Test</string> <string name="searchable_hint">A search hint</string> + + <!-- InterrogationActivity --> + <string name="button1">Button1</string> + <string name="button2">Button2</string> + <string name="button3">Button3</string> + <string name="button4">Button4</string> + <string name="button5">Button5</string> + <string name="button6">Button6</string> + <string name="button7">Button7</string> + <string name="button8">Button8</string> + <string name="button9">Button9</string> + </resources> diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java new file mode 100644 index 0000000..b4a0581 --- /dev/null +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2011 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.accessibilityservice; + +import com.android.frameworks.coretests.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; + +/** + * Activity for testing the accessibility APIs for "interrogation" of + * the screen content. These APIs allow exploring the screen and + * requesting an action to be performed on a given view from an + * AccessiiblityService. + */ +public class InterrogationActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.interrogation_activity); + + findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + /* do nothing */ + } + }); + findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() { + public boolean onLongClick(View v) { + return true; + } + }); + } +} diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java new file mode 100644 index 0000000..a20cc1f --- /dev/null +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java @@ -0,0 +1,464 @@ +/** + * Copyright (C) 2011 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.accessibilityservice; + +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT; + +import com.android.frameworks.coretests.R; + +import android.content.Context; +import android.graphics.Rect; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.provider.Settings; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityManager; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * Activity for testing the accessibility APIs for "interrogation" of + * the screen content. These APIs allow exploring the screen and + * requesting an action to be performed on a given view from an + * AccessiiblityService. + */ +public class InterrogationActivityTest + extends ActivityInstrumentationTestCase2<InterrogationActivity> { + + // Timeout before give up wait for the system to process an accessibility setting change. + private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000; + + // Helpers to figure out the first and last test methods + // This is a workaround for the lack of such support in JUnit3 + private static int sTestMethodCount; + private static int sExecutedTestMethodCount; + + // Handle to a connection to the AccessibilityManagerService + private static IAccessibilityServiceConnection sConnection; + + // The last received accessibility event + private static volatile AccessibilityEvent sLastAccessibilityEvent; + + public InterrogationActivityTest() { + super(InterrogationActivity.class); + sTestMethodCount = getTestMethodCount(); + } + + @LargeTest + public void testFindAccessibilityNodeInfoByViewId() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertNotNull(button); + assertEquals(0, button.getChildCount()); + + // bounds + Rect bounds = new Rect(); + button.getBounds(bounds); + assertEquals(0, bounds.left); + assertEquals(0, bounds.top); + assertEquals(73, bounds.right); + assertEquals(48, bounds.bottom); + + // char sequence attributes + assertEquals("com.android.frameworks.coretests", button.getPackageName()); + assertEquals("android.widget.Button", button.getClassName()); + assertEquals("Button5", button.getText()); + assertNull(button.getContentDescription()); + + // boolean attributes + assertTrue(button.isFocusable()); + assertTrue(button.isClickable()); + assertTrue(button.isEnabled()); + assertFalse(button.isFocused()); + assertTrue(button.isClickable()); + assertFalse(button.isPassword()); + assertFalse(button.isSelected()); + assertFalse(button.isCheckable()); + assertFalse(button.isChecked()); + + // actions + assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION, + button.getActions()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testFindAccessibilityNodeInfoByViewText() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view by text + List<AccessibilityNodeInfo> buttons = + getConnection().findAccessibilityNodeInfosByViewText("butto"); + assertEquals(9, buttons.size()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testTraverseAllViews() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // make list of expected nodes + List<String> classNameAndTextList = new ArrayList<String>(); + classNameAndTextList.add("android.widget.LinearLayout"); + classNameAndTextList.add("android.widget.LinearLayout"); + classNameAndTextList.add("android.widget.LinearLayout"); + classNameAndTextList.add("android.widget.LinearLayout"); + classNameAndTextList.add("android.widget.ButtonButton1"); + classNameAndTextList.add("android.widget.ButtonButton2"); + classNameAndTextList.add("android.widget.ButtonButton3"); + classNameAndTextList.add("android.widget.ButtonButton4"); + classNameAndTextList.add("android.widget.ButtonButton5"); + classNameAndTextList.add("android.widget.ButtonButton6"); + classNameAndTextList.add("android.widget.ButtonButton7"); + classNameAndTextList.add("android.widget.ButtonButton8"); + classNameAndTextList.add("android.widget.ButtonButton9"); + + AccessibilityNodeInfo root = getConnection().findAccessibilityNodeInfoByViewId( + R.id.root); + assertNotNull("We must find the existing root.", root); + + Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>(); + fringe.add(root); + + // do a BFS traversal and check nodes + while (!fringe.isEmpty()) { + AccessibilityNodeInfo current = fringe.poll(); + + CharSequence className = current.getClassName(); + CharSequence text = current.getText(); + String receivedClassNameAndText = className.toString() + + ((text != null) ? text.toString() : ""); + String expectedClassNameAndText = classNameAndTextList.remove(0); + + assertEquals("Did not get the expected node info", + expectedClassNameAndText, receivedClassNameAndText); + + final int childCount = current.getChildCount(); + for (int i = 0; i < childCount; i++) { + AccessibilityNodeInfo child = current.getChild(i); + fringe.add(child); + } + } + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testPerformAccessibilityActionFocus() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not focused + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertFalse(button.isFocused()); + + // focus the view + assertTrue(button.performAction(ACTION_FOCUS)); + + // find the view again and make sure it is focused + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertTrue(button.isFocused()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testPerformAccessibilityActionClearFocus() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not focused + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertFalse(button.isFocused()); + + // focus the view + assertTrue(button.performAction(ACTION_FOCUS)); + + // find the view again and make sure it is focused + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertTrue(button.isFocused()); + + // unfocus the view + assertTrue(button.performAction(ACTION_CLEAR_FOCUS)); + + // find the view again and make sure it is not focused + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertFalse(button.isFocused()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testPerformAccessibilityActionSelect() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not selected + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertFalse(button.isSelected()); + + // select the view + assertTrue(button.performAction(ACTION_SELECT)); + + // find the view again and make sure it is selected + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertTrue(button.isSelected()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testPerformAccessibilityActionClearSelection() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not selected + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertFalse(button.isSelected()); + + // select the view + assertTrue(button.performAction(ACTION_SELECT)); + + // find the view again and make sure it is selected + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertTrue(button.isSelected()); + + // unselect the view + assertTrue(button.performAction(ACTION_CLEAR_SELECTION)); + + // find the view again and make sure it is not selected + button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + assertFalse(button.isSelected()); + } finally { + afterClassIfNeeded(); + } + } + + @LargeTest + public void testAccessibilityEventGetSource() throws Exception { + beforeClassIfNeeded(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not focused + AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( + R.id.button5); + assertFalse(button.isSelected()); + + // focus the view + assertTrue(button.performAction(ACTION_FOCUS)); + SystemClock.sleep(200); + + // check that last event source + AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource(); + assertNotNull(source); + + // bounds + Rect buttonBounds = new Rect(); + button.getBounds(buttonBounds); + Rect sourceBounds = new Rect(); + source.getBounds(sourceBounds); + + assertEquals(buttonBounds.left, sourceBounds.left); + assertEquals(buttonBounds.right, sourceBounds.right); + assertEquals(buttonBounds.top, sourceBounds.top); + assertEquals(buttonBounds.bottom, sourceBounds.bottom); + + // char sequence attributes + assertEquals(button.getPackageName(), source.getPackageName()); + assertEquals(button.getClassName(), source.getClassName()); + assertEquals(button.getText(), source.getText()); + assertSame(button.getContentDescription(), source.getContentDescription()); + + // boolean attributes + assertSame(button.isFocusable(), source.isFocusable()); + assertSame(button.isClickable(), source.isClickable()); + assertSame(button.isEnabled(), source.isEnabled()); + assertNotSame(button.isFocused(), source.isFocused()); + assertSame(button.isLongClickable(), source.isLongClickable()); + assertSame(button.isPassword(), source.isPassword()); + assertSame(button.isSelected(), source.isSelected()); + assertSame(button.isCheckable(), source.isCheckable()); + assertSame(button.isChecked(), source.isChecked()); + } finally { + afterClassIfNeeded(); + } + } + + @Override + protected void scrubClass(Class<?> testCaseClass) { + /* intentionally do not scrub */ + } + + /** + * Sets accessibility in a given state by writing the state to the + * settings and waiting until the accessibility manager service picks + * it up for max {@link #TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING}. + * + * @param state The accessibility state. + * @throws Exception If any error occurs. + */ + private void ensureAccessibilityState(boolean state) throws Exception { + Context context = getInstrumentation().getContext(); + // If the local manager ready => nothing to do. + AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context); + if (accessibilityManager.isEnabled() == state) { + return; + } + synchronized (this) { + // Check if the system already knows about the desired state. + final boolean currentState = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED) == 1; + if (currentState != state) { + // Make sure we wake ourselves as the desired state is propagated. + accessibilityManager.addAccessibilityStateChangeListener( + new AccessibilityManager.AccessibilityStateChangeListener() { + public void onAccessibilityStateChanged(boolean enabled) { + synchronized (this) { + notifyAll(); + } + } + }); + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, state ? 1 : 0); + } + // No while one attempt and that is it. + try { + wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING); + } catch (InterruptedException ie) { + /* ignore */ + } + } + if (accessibilityManager.isEnabled() != state) { + throw new IllegalStateException("Could not set accessibility state to: " + state); + } + } + + /** + * Execute some set up code before any test method. + * + * NOTE: I miss Junit4's @BeforeClass + * + * @throws Exception If an error occurs. + */ + private void beforeClassIfNeeded() throws Exception { + sExecutedTestMethodCount++; + if (sExecutedTestMethodCount == 1) { + ensureAccessibilityState(true); + } + } + + /** + * Execute some clean up code after all test methods. + * + * NOTE: I miss Junit4's @AfterClass + * + * @throws Exception If an error occurs. + */ + public void afterClassIfNeeded() throws Exception { + if (sExecutedTestMethodCount == sTestMethodCount) { + sExecutedTestMethodCount = 0; + ensureAccessibilityState(false); + } + } + + private static IAccessibilityServiceConnection getConnection() throws Exception { + if (sConnection == null) { + IEventListener listener = new IEventListener.Stub() { + public void setConnection(IAccessibilityServiceConnection connection) + throws RemoteException { + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; + info.notificationTimeout = 0; + info.flags = AccessibilityServiceInfo.DEFAULT; + connection.setServiceInfo(info); + } + + public void onInterrupt() {} + + public void onAccessibilityEvent(AccessibilityEvent event) { + sLastAccessibilityEvent= AccessibilityEvent.obtain(event); + } + }; + IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + sConnection = manager.registerEventListener(listener); + } + return sConnection; + } + + /** + * @return The number of test methods. + */ + private int getTestMethodCount() { + int testMethodCount = 0; + for (Method method : getClass().getMethods()) { + final int modifiers = method.getModifiers(); + if (method.getName().startsWith("test") + && (modifiers & Modifier.PUBLIC) != 0 + && (modifiers & Modifier.STATIC) == 0) { + testMethodCount++; + } + } + return testMethodCount; + } +} diff --git a/core/tests/coretests/src/android/view/GravityTest.java b/core/tests/coretests/src/android/view/GravityTest.java index 180a390..2a7a64f 100644 --- a/core/tests/coretests/src/android/view/GravityTest.java +++ b/core/tests/coretests/src/android/view/GravityTest.java @@ -59,11 +59,11 @@ public class GravityTest extends AndroidTestCase { assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, false); assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, true); - assertOneGravity(Gravity.LEFT, Gravity.BEFORE, false); - assertOneGravity(Gravity.RIGHT, Gravity.BEFORE, true); + assertOneGravity(Gravity.LEFT, Gravity.START, false); + assertOneGravity(Gravity.RIGHT, Gravity.START, true); - assertOneGravity(Gravity.RIGHT, Gravity.AFTER, false); - assertOneGravity(Gravity.LEFT, Gravity.AFTER, true); + assertOneGravity(Gravity.RIGHT, Gravity.END, false); + assertOneGravity(Gravity.LEFT, Gravity.END, true); } private void assertOneGravity(int expected, int initial, boolean isRtl) { |