Merge "Fix ssid fetch"
diff --git a/Android.mk b/Android.mk
index cacdee9..3b2d32d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -60,7 +60,7 @@
## READ ME: ########################################################
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
- core/java/android/accessibilityservice/IEventListener.aidl \
+ core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
core/java/android/accounts/IAccountManager.aidl \
core/java/android/accounts/IAccountManagerResponse.aidl \
core/java/android/accounts/IAccountAuthenticator.aidl \
@@ -111,6 +111,7 @@
core/java/android/database/IContentObserver.aidl \
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/input/IInputManager.aidl \
+ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
@@ -328,7 +329,10 @@
)
# include definition of libcore_to_document
-include $(LOCAL_PATH)/../../libcore/Docs.mk
+include libcore/Docs.mk
+
+# include definition of junit_to_document
+include external/junit/Common.mk
non_base_dirs := \
../../external/apache-http/src/org/apache/http
@@ -352,7 +356,8 @@
# Common sources for doc check and api check
common_src_files := \
$(call find-other-html-files, $(html_dirs)) \
- $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore))
+ $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \
+ $(addprefix ../../external/junit/, $(call junit_to_document, $(LOCAL_PATH)/../../external/junit))
# These are relative to frameworks/base
framework_docs_LOCAL_SRC_FILES := \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1cd5d01..939c117 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -126,6 +126,9 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libRS_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libRSDriver_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/host/$(HOST_PREBUILT_TAG)/obj/STATIC_LIBRARIES/libRS_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/accessibilityservice/IEventListener.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/accessibilityservice/IEventListener.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/accessibility/IAccessibilityManager.P)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/16.txt b/api/16.txt
index aa23b4d..9e9f880 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -9513,7 +9513,7 @@
method public abstract void onSensorChanged(int, float[]);
}
- public class SensorManager {
+ public abstract class SensorManager {
method public static float getAltitude(float, float);
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
@@ -15190,11 +15190,11 @@
method public abstract void released();
}
- public class Vibrator {
- method public void cancel();
- method public boolean hasVibrator();
- method public void vibrate(long);
- method public void vibrate(long[], int);
+ public abstract class Vibrator {
+ method public abstract void cancel();
+ method public abstract boolean hasVibrator();
+ method public abstract void vibrate(long);
+ method public abstract void vibrate(long[], int);
}
public class WorkSource implements android.os.Parcelable {
@@ -24806,7 +24806,7 @@
enum_constant public static final android.webkit.ConsoleMessage.MessageLevel WARNING;
}
- public final class CookieManager {
+ public class CookieManager {
method public synchronized boolean acceptCookie();
method public static boolean allowFileSchemeCookies();
method public java.lang.String getCookie(java.lang.String);
@@ -24838,8 +24838,7 @@
method public abstract void onDownloadStart(java.lang.String, java.lang.String, java.lang.String, java.lang.String, long);
}
- public final class GeolocationPermissions {
- ctor public GeolocationPermissions();
+ public class GeolocationPermissions {
method public void allow(java.lang.String);
method public void clear(java.lang.String);
method public void clearAll();
@@ -24865,8 +24864,6 @@
public class JsResult {
method public final void cancel();
method public final void confirm();
- method protected final void wakeUp();
- field protected boolean mResult;
}
public class MimeTypeMap {
@@ -24959,7 +24956,7 @@
method public java.lang.String getUrl();
}
- public final class WebIconDatabase {
+ public class WebIconDatabase {
method public void close();
method public static android.webkit.WebIconDatabase getInstance();
method public void open(java.lang.String);
@@ -25128,8 +25125,7 @@
enum_constant public static final android.webkit.WebSettings.ZoomDensity MEDIUM;
}
- public final class WebStorage {
- ctor public WebStorage();
+ public class WebStorage {
method public void deleteAllData();
method public void deleteOrigin(java.lang.String);
method public static android.webkit.WebStorage getInstance();
diff --git a/api/current.txt b/api/current.txt
index b2a8b2d..00cf365 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -534,6 +534,7 @@
field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
+ field public static final int importantForAccessibility = 16843699; // 0x10103b3
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1993,11 +1994,23 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
+ method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
+ method protected void onGesture(int);
method public abstract void onInterrupt();
method protected void onServiceConnected();
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+ field public static final int GESTURE_CLOCKWISE_CIRCLE = 9; // 0x9
+ field public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10; // 0xa
+ field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
+ field public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; // 0x8
+ field public static final int GESTURE_SWIPE_LEFT = 3; // 0x3
+ field public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; // 0x5
+ field public static final int GESTURE_SWIPE_RIGHT = 4; // 0x4
+ field public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; // 0x6
+ field public static final int GESTURE_SWIPE_UP = 1; // 0x1
+ field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
@@ -2022,6 +2035,7 @@
field public static final int FEEDBACK_HAPTIC = 2; // 0x2
field public static final int FEEDBACK_SPOKEN = 1; // 0x1
field public static final int FEEDBACK_VISUAL = 8; // 0x8
+ field public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
field public int eventTypes;
field public int feedbackType;
field public int flags;
@@ -2882,6 +2896,7 @@
public class ActivityOptions {
method public void join(android.app.ActivityOptions);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+ method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public android.os.Bundle toBundle();
}
@@ -3251,6 +3266,7 @@
method public android.app.DownloadManager.Request addRequestHeader(java.lang.String, java.lang.String);
method public void allowScanningByMediaScanner();
method public android.app.DownloadManager.Request setAllowedNetworkTypes(int);
+ method public android.app.DownloadManager.Request setAllowedOverMetered(boolean);
method public android.app.DownloadManager.Request setAllowedOverRoaming(boolean);
method public android.app.DownloadManager.Request setDescription(java.lang.CharSequence);
method public android.app.DownloadManager.Request setDestinationInExternalFilesDir(android.content.Context, java.lang.String, java.lang.String);
@@ -3686,6 +3702,7 @@
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final int STREAM_DEFAULT = -1; // 0xffffffff
field public int audioStreamType;
+ field public android.widget.RemoteViews bigContentView;
field public android.app.PendingIntent contentIntent;
field public android.widget.RemoteViews contentView;
field public int defaults;
@@ -3708,6 +3725,18 @@
field public long when;
}
+ public static class Notification.BigPictureStyle {
+ ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+ method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
+ method public android.app.Notification build();
+ }
+
+ public static class Notification.BigTextStyle {
+ ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+ method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
+ method public android.app.Notification build();
+ }
+
public static class Notification.Builder {
ctor public Notification.Builder(android.content.Context);
method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
@@ -3737,6 +3766,7 @@
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+ method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setUsesIntruderAlert(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setWhen(long);
@@ -4819,6 +4849,7 @@
method public android.content.ClipDescription getDescription();
method public android.content.ClipData.Item getItemAt(int);
method public int getItemCount();
+ method public static android.content.ClipData newHtmlText(java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
method public static android.content.ClipData newIntent(java.lang.CharSequence, android.content.Intent);
method public static android.content.ClipData newPlainText(java.lang.CharSequence, java.lang.CharSequence);
method public static android.content.ClipData newRawUri(java.lang.CharSequence, android.net.Uri);
@@ -4829,10 +4860,15 @@
public static class ClipData.Item {
ctor public ClipData.Item(java.lang.CharSequence);
+ ctor public ClipData.Item(java.lang.CharSequence, java.lang.String);
ctor public ClipData.Item(android.content.Intent);
ctor public ClipData.Item(android.net.Uri);
ctor public ClipData.Item(java.lang.CharSequence, android.content.Intent, android.net.Uri);
+ ctor public ClipData.Item(java.lang.CharSequence, java.lang.String, android.content.Intent, android.net.Uri);
+ method public java.lang.String coerceToHtmlText(android.content.Context);
+ method public java.lang.CharSequence coerceToStyledText(android.content.Context);
method public java.lang.CharSequence coerceToText(android.content.Context);
+ method public java.lang.String getHtmlText();
method public android.content.Intent getIntent();
method public java.lang.CharSequence getText();
method public android.net.Uri getUri();
@@ -4850,6 +4886,7 @@
method public boolean hasMimeType(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
field public static final java.lang.String MIMETYPE_TEXT_URILIST = "text/uri-list";
@@ -5687,6 +5724,7 @@
field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+ field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_INTENT = "android.intent.extra.INTENT";
@@ -8981,6 +9019,8 @@
public class SurfaceTexture {
ctor public SurfaceTexture(int);
+ method public void attachToGLContext(int);
+ method public void detachFromGLContext();
method public long getTimestamp();
method public void getTransformMatrix(float[]);
method public void release();
@@ -9453,6 +9493,7 @@
method public static android.hardware.Camera open();
method public final void reconnect() throws java.io.IOException;
method public final void release();
+ method public void setAutoFocusMoveCallback(android.hardware.Camera.AutoFocusMoveCallback);
method public final void setDisplayOrientation(int);
method public final void setErrorCallback(android.hardware.Camera.ErrorCallback);
method public final void setFaceDetectionListener(android.hardware.Camera.FaceDetectionListener);
@@ -9488,6 +9529,10 @@
method public abstract void onAutoFocus(boolean, android.hardware.Camera);
}
+ public static abstract interface Camera.AutoFocusMoveCallback {
+ method public abstract void onAutoFocusMoving(boolean, android.hardware.Camera);
+ }
+
public static class Camera.CameraInfo {
ctor public Camera.CameraInfo();
field public static final int CAMERA_FACING_BACK = 0; // 0x0
@@ -9738,7 +9783,7 @@
method public abstract void onSensorChanged(int, float[]);
}
- public class SensorManager {
+ public abstract class SensorManager {
method public static float getAltitude(float, float);
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
@@ -9822,10 +9867,20 @@
package android.hardware.input {
public final class InputManager {
+ method public android.view.InputDevice getInputDevice(int);
+ method public int[] getInputDeviceIds();
+ method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
+ method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener);
field public static final java.lang.String ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
field public static final java.lang.String META_DATA_KEYBOARD_LAYOUTS = "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
}
+ public static abstract interface InputManager.InputDeviceListener {
+ method public abstract void onInputDeviceAdded(int);
+ method public abstract void onInputDeviceChanged(int);
+ method public abstract void onInputDeviceRemoved(int);
+ }
+
}
package android.hardware.usb {
@@ -10884,6 +10939,91 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
+ public final class MediaCodec {
+ method public void configure(java.util.Map<java.lang.String, java.lang.Object>, android.view.Surface, android.media.MediaCrypto, int);
+ method public static android.media.MediaCodec createByCodecName(java.lang.String);
+ method public static android.media.MediaCodec createDecoderByType(java.lang.String);
+ method public static android.media.MediaCodec createEncoderByType(java.lang.String);
+ method public final int dequeueInputBuffer(long);
+ method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
+ method public final void flush();
+ method public java.nio.ByteBuffer[] getInputBuffers();
+ method public java.nio.ByteBuffer[] getOutputBuffers();
+ method public final java.util.Map<java.lang.String, java.lang.Object> getOutputFormat();
+ method public final void queueInputBuffer(int, int, int, long, int);
+ method public final void queueSecureInputBuffer(int, int, int[], int[], int, byte[], byte[], int, long, int);
+ method public final void release();
+ method public final void releaseOutputBuffer(int, boolean);
+ method public final void start();
+ method public final void stop();
+ field public static int CONFIGURE_FLAG_ENCODE;
+ field public static final int FLAG_CODECCONFIG = 2; // 0x2
+ field public static final int FLAG_EOS = 4; // 0x4
+ field public static final int FLAG_SYNCFRAME = 1; // 0x1
+ field public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
+ field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
+ field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
+ field public static final int MODE_AES_CTR = 1; // 0x1
+ field public static final int MODE_UNENCRYPTED = 0; // 0x0
+ }
+
+ public static final class MediaCodec.BufferInfo {
+ ctor public MediaCodec.BufferInfo();
+ method public void set(int, int, long, int);
+ field public int flags;
+ field public int offset;
+ field public long presentationTimeUs;
+ field public int size;
+ }
+
+ public final class MediaCodecList {
+ method public static final int countCodecs();
+ method public static final android.media.MediaCodecList.CodecCapabilities getCodecCapabilities(int, java.lang.String);
+ method public static final java.lang.String getCodecName(int);
+ method public static final java.lang.String[] getSupportedTypes(int);
+ method public static final boolean isEncoder(int);
+ }
+
+ public static final class MediaCodecList.CodecCapabilities {
+ ctor public MediaCodecList.CodecCapabilities();
+ field public int[] colorFormats;
+ field public android.media.MediaCodecList.CodecProfileLevel[] profileLevels;
+ }
+
+ public static final class MediaCodecList.CodecProfileLevel {
+ ctor public MediaCodecList.CodecProfileLevel();
+ field public int level;
+ field public int profile;
+ }
+
+ public final class MediaCrypto {
+ ctor public MediaCrypto(byte[], byte[]);
+ method public static final boolean isCryptoSchemeSupported(byte[]);
+ method public final void release();
+ method public final boolean requiresSecureDecoderComponent(java.lang.String);
+ }
+
+ public final class MediaExtractor {
+ ctor public MediaExtractor();
+ method public boolean advance();
+ method public int countTracks();
+ method public int getSampleFlags();
+ method public long getSampleTime();
+ method public int getSampleTrackIndex();
+ method public java.util.Map<java.lang.String, java.lang.Object> getTrackFormat(int);
+ method public int readSampleData(java.nio.ByteBuffer, int);
+ method public final void release();
+ method public void seekTo(long);
+ method public void selectTrack(int);
+ method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>);
+ method public final void setDataSource(java.lang.String);
+ method public final void setDataSource(java.io.FileDescriptor);
+ method public final void setDataSource(java.io.FileDescriptor, long, long);
+ field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
+ field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
+ }
+
public class MediaMetadataRetriever {
ctor public MediaMetadataRetriever();
method public java.lang.String extractMetadata(int);
@@ -11782,6 +11922,7 @@
method public deprecated boolean getBackgroundDataSetting();
method public android.net.NetworkInfo getNetworkInfo(int);
method public int getNetworkPreference();
+ method public boolean isActiveNetworkMetered();
method public static boolean isNetworkTypeValid(int);
method public boolean requestRouteToHost(int, int);
method public void setNetworkPreference(int);
@@ -12876,6 +13017,8 @@
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method public boolean isEnabled();
method public boolean isNdefPushEnabled();
+ method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
+ method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
@@ -12887,6 +13030,10 @@
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
}
+ public static abstract interface NfcAdapter.CreateBeamUrisCallback {
+ method public abstract android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
+ }
+
public static abstract interface NfcAdapter.CreateNdefMessageCallback {
method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
}
@@ -15471,11 +15618,11 @@
ctor public TransactionTooLargeException();
}
- public class Vibrator {
- method public void cancel();
- method public boolean hasVibrator();
- method public void vibrate(long);
- method public void vibrate(long[], int);
+ public abstract class Vibrator {
+ method public abstract void cancel();
+ method public abstract boolean hasVibrator();
+ method public abstract void vibrate(long);
+ method public abstract void vibrate(long[], int);
}
public class WorkSource implements android.os.Parcelable {
@@ -16824,7 +16971,7 @@
method public static android.net.Uri getLookupUri(android.content.ContentResolver, android.net.Uri);
method public static android.net.Uri getLookupUri(long, java.lang.String);
method public static android.net.Uri lookupContact(android.content.ContentResolver, android.net.Uri);
- method public static void markAsContacted(android.content.ContentResolver, long);
+ method public static deprecated void markAsContacted(android.content.ContentResolver, long);
method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri, boolean);
method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri);
field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -16915,6 +17062,7 @@
public static final class ContactsContract.DataUsageFeedback {
ctor public ContactsContract.DataUsageFeedback();
+ field public static final android.net.Uri DELETE_USAGE_URI;
field public static final android.net.Uri FEEDBACK_URI;
field public static final java.lang.String USAGE_TYPE = "type";
field public static final java.lang.String USAGE_TYPE_CALL = "call";
@@ -20370,6 +20518,7 @@
}
public class Html {
+ method public static java.lang.String escapeHtml(java.lang.CharSequence);
method public static android.text.Spanned fromHtml(java.lang.String);
method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
method public static java.lang.String toHtml(android.text.Spanned);
@@ -22317,6 +22466,7 @@
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
method public java.lang.String getName();
method public int getSources();
+ method public android.os.Vibrator getVibrator();
method public boolean isVirtual();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -23243,6 +23393,7 @@
method public android.graphics.Canvas lockCanvas(android.graphics.Rect);
method protected final void onDraw(android.graphics.Canvas);
method public void setOpaque(boolean);
+ method public void setSurfaceTexture(android.graphics.SurfaceTexture);
method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener);
method public void setTransform(android.graphics.Matrix);
method public void unlockCanvasAndPost(android.graphics.Canvas);
@@ -23281,6 +23432,7 @@
ctor public View(android.content.Context);
ctor public View(android.content.Context, android.util.AttributeSet);
ctor public View(android.content.Context, android.util.AttributeSet, int);
+ method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
@@ -23383,6 +23535,7 @@
method public int getHorizontalFadingEdgeLength();
method protected int getHorizontalScrollbarHeight();
method public int getId();
+ method public int getImportantForAccessibility();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLayerType();
@@ -23416,6 +23569,7 @@
method public int getPaddingStart();
method public int getPaddingTop();
method public final android.view.ViewParent getParent();
+ method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
method public int getResolvedLayoutDirection();
@@ -23571,6 +23725,7 @@
method public void onWindowSystemUiVisibilityChanged(int);
method protected void onWindowVisibilityChanged(int);
method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
+ method public boolean performAccessibilityAction(int);
method public boolean performClick();
method public boolean performHapticFeedback(int);
method public boolean performHapticFeedback(int, int);
@@ -23642,6 +23797,7 @@
method public void setHorizontalScrollBarEnabled(boolean);
method public void setHovered(boolean);
method public void setId(int);
+ method public void setImportantForAccessibility(int);
method public void setKeepScreenOn(boolean);
method public void setLayerType(int, android.graphics.Paint);
method public void setLayoutDirection(int);
@@ -23716,6 +23872,14 @@
method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
method public boolean willNotCacheDrawing();
method public boolean willNotDraw();
+ field public static final int ACCESSIBILITY_FOCUS_BACKWARD = 4097; // 0x1001
+ field public static final int ACCESSIBILITY_FOCUS_DOWN = 4226; // 0x1082
+ field public static final int ACCESSIBILITY_FOCUS_FORWARD = 4098; // 0x1002
+ field public static final int ACCESSIBILITY_FOCUS_IN = 4100; // 0x1004
+ field public static final int ACCESSIBILITY_FOCUS_LEFT = 4113; // 0x1011
+ field public static final int ACCESSIBILITY_FOCUS_OUT = 4104; // 0x1008
+ field public static final int ACCESSIBILITY_FOCUS_RIGHT = 4162; // 0x1042
+ field public static final int ACCESSIBILITY_FOCUS_UP = 4129; // 0x1021
field public static final android.util.Property ALPHA;
field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -23737,6 +23901,7 @@
field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] FOCUSED_STATE_SET;
field protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
+ field public static final int FOCUS_ACCESSIBILITY = 4096; // 0x1000
field public static final int FOCUS_BACKWARD = 1; // 0x1
field public static final int FOCUS_DOWN = 130; // 0x82
field public static final int FOCUS_FORWARD = 2; // 0x2
@@ -23745,6 +23910,9 @@
field public static final int FOCUS_UP = 33; // 0x21
field public static final int GONE = 8; // 0x8
field public static final int HAPTIC_FEEDBACK_ENABLED = 268435456; // 0x10000000
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -24050,6 +24218,7 @@
method public android.view.View getFocusedChild();
method public android.view.animation.LayoutAnimationController getLayoutAnimation();
method public android.view.animation.Animation.AnimationListener getLayoutAnimationListener();
+ method public int getLayoutMode();
method public android.animation.LayoutTransition getLayoutTransition();
method public int getPersistentDrawingCache();
method public int indexOfChild(android.view.View);
@@ -24097,6 +24266,7 @@
method public void setDescendantFocusability(int);
method public void setLayoutAnimation(android.view.animation.LayoutAnimationController);
method public void setLayoutAnimationListener(android.view.animation.Animation.AnimationListener);
+ method public void setLayoutMode(int);
method public void setLayoutTransition(android.animation.LayoutTransition);
method public void setMotionEventSplittingEnabled(boolean);
method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
@@ -24109,9 +24279,11 @@
method public void startViewTransition(android.view.View);
method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
+ field public static final int COMPONENT_BOUNDS = 0; // 0x0
field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
field public static final int FOCUS_BEFORE_DESCENDANTS = 131072; // 0x20000
field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
+ field public static final int LAYOUT_BOUNDS = 1; // 0x1
field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
@@ -24169,6 +24341,7 @@
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
method public abstract android.view.ViewParent getParent();
+ method public abstract android.view.ViewParent getParentForAccessibility();
method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public abstract boolean isLayoutRequested();
@@ -24570,6 +24743,8 @@
field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40
field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+ field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+ field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
field public static final int TYPE_VIEW_CLICKED = 1; // 0x1
field public static final int TYPE_VIEW_FOCUSED = 8; // 0x8
field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
@@ -24610,6 +24785,8 @@
method public void addChild(android.view.View, int);
method public int describeContents();
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
+ method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+ method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public int getActions();
method public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
@@ -24621,6 +24798,7 @@
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method public java.lang.CharSequence getText();
method public int getWindowId();
+ method public boolean isAccessibilityFocused();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -24637,6 +24815,7 @@
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
method public boolean performAction(int);
method public void recycle();
+ method public void setAccessibilityFocused(boolean);
method public void setBoundsInParent(android.graphics.Rect);
method public void setBoundsInScreen(android.graphics.Rect);
method public void setCheckable(boolean);
@@ -24658,16 +24837,23 @@
method public void setSource(android.view.View, int);
method public void setText(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTION_ACCESSIBILITY_FOCUS = 16; // 0x10
+ field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 32; // 0x20
field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+ field public static final int ACTION_CLICK = 64; // 0x40
field public static final int ACTION_FOCUS = 1; // 0x1
field public static final int ACTION_SELECT = 4; // 0x4
field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+ field public static final int FOCUS_INPUT = 1; // 0x1
}
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
+ method public android.view.accessibility.AccessibilityNodeInfo accessibilityFocusSearch(int, int);
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
+ method public android.view.accessibility.AccessibilityNodeInfo findAccessibilitiyFocus(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public boolean performAccessibilityAction(int, int);
}
@@ -25444,7 +25630,7 @@
enum_constant public static final android.webkit.ConsoleMessage.MessageLevel WARNING;
}
- public final class CookieManager {
+ public class CookieManager {
method public synchronized boolean acceptCookie();
method public static boolean allowFileSchemeCookies();
method public java.lang.String getCookie(java.lang.String);
@@ -25476,8 +25662,7 @@
method public abstract void onDownloadStart(java.lang.String, java.lang.String, java.lang.String, java.lang.String, long);
}
- public final class GeolocationPermissions {
- ctor public GeolocationPermissions();
+ public class GeolocationPermissions {
method public void allow(java.lang.String);
method public void clear(java.lang.String);
method public void clearAll();
@@ -25503,8 +25688,6 @@
public class JsResult {
method public final void cancel();
method public final void confirm();
- method protected final void wakeUp();
- field protected boolean mResult;
}
public class MimeTypeMap {
@@ -25597,7 +25780,7 @@
method public java.lang.String getUrl();
}
- public final class WebIconDatabase {
+ public class WebIconDatabase {
method public void close();
method public static android.webkit.WebIconDatabase getInstance();
method public void open(java.lang.String);
@@ -25766,8 +25949,7 @@
enum_constant public static final android.webkit.WebSettings.ZoomDensity MEDIUM;
}
- public final class WebStorage {
- ctor public WebStorage();
+ public class WebStorage {
method public void deleteAllData();
method public void deleteOrigin(java.lang.String);
method public static android.webkit.WebStorage getInstance();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ddd7f7c..3da35d3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -19,17 +19,22 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.LocaleUtil;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.os.HandlerCaller;
+import java.util.Locale;
+
/**
* An accessibility service runs in the background and receives callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -202,12 +207,65 @@
* @see android.view.accessibility.AccessibilityManager
*/
public abstract class AccessibilityService extends Service {
+
+ /**
+ * The user has performed a swipe up gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_UP = 1;
+
+ /**
+ * The user has performed a swipe down gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_DOWN = 2;
+
+ /**
+ * The user has performed a swipe left gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_LEFT = 3;
+
+ /**
+ * The user has performed a swipe right gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_RIGHT = 4;
+
+ /**
+ * The user has performed a swipe left and right gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
+
+ /**
+ * The user has performed a swipe right and left gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
+
+ /**
+ * The user has performed a swipe up and down gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
+
+ /**
+ * The user has performed a swipe down and up gesture on the touch screen.
+ */
+ public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
+
+ /**
+ * The user has performed a clockwise circle gesture on the touch screen.
+ */
+ public static final int GESTURE_CLOCKWISE_CIRCLE = 9;
+
+ /**
+ * The user has performed a counter clockwise circle gesture on the touch screen.
+ */
+ public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
public static final String SERVICE_INTERFACE =
"android.accessibilityservice.AccessibilityService";
+ private static final int UNDEFINED = -1;
+
/**
* Name under which an AccessibilityService component publishes information
* about itself. This meta-data must reference an XML resource containing an
@@ -233,12 +291,15 @@
public void onInterrupt();
public void onServiceConnected();
public void onSetConnectionId(int connectionId);
+ public void onGesture(int gestureId);
}
private int mConnectionId;
private AccessibilityServiceInfo mInfo;
+ private int mLayoutDirection;
+
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
*
@@ -264,6 +325,106 @@
}
/**
+ * Called by the system when the user performs a specific gesture on the
+ * touch screen.
+ *
+ * @param gestureId The unique id of the performed gesture.
+ *
+ * @see #GESTURE_SWIPE_UP
+ * @see #GESTURE_SWIPE_DOWN
+ * @see #GESTURE_SWIPE_LEFT
+ * @see #GESTURE_SWIPE_RIGHT
+ * @see #GESTURE_SWIPE_UP_AND_DOWN
+ * @see #GESTURE_SWIPE_DOWN_AND_UP
+ * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
+ * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
+ * @see #GESTURE_CLOCKWISE_CIRCLE
+ * @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE
+ */
+ protected void onGesture(int gestureId) {
+ // TODO: Describe the default gesture processing in the javaDoc once it is finalized.
+
+ // Cache the id to avoid locking
+ final int connectionId = mConnectionId;
+ if (connectionId == UNDEFINED) {
+ throw new IllegalStateException("AccessibilityService not connected."
+ + " Did you receive a call of onServiceConnected()?");
+ }
+ AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByAccessibilityId(connectionId,
+ AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ if (root == null) {
+ return;
+ }
+ AccessibilityNodeInfo current = root.findFocus(View.FOCUS_ACCESSIBILITY);
+ if (current == null) {
+ current = root;
+ }
+ AccessibilityNodeInfo next = null;
+ switch (gestureId) {
+ case GESTURE_SWIPE_UP: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_OUT);
+ } break;
+ case GESTURE_SWIPE_DOWN: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_IN);
+ } break;
+ case GESTURE_SWIPE_LEFT: {
+ if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
+ } else { // LAYOUT_DIRECTION_RTL
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
+ }
+ } break;
+ case GESTURE_SWIPE_RIGHT: {
+ if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
+ } else { // LAYOUT_DIRECTION_RTL
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
+ }
+ } break;
+ case GESTURE_SWIPE_UP_AND_DOWN: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_UP);
+ } break;
+ case GESTURE_SWIPE_DOWN_AND_UP: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_DOWN);
+ } break;
+ case GESTURE_SWIPE_LEFT_AND_RIGHT: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_LEFT);
+ } break;
+ case GESTURE_SWIPE_RIGHT_AND_LEFT: {
+ next = current.focusSearch(View.ACCESSIBILITY_FOCUS_RIGHT);
+ } break;
+ }
+ if (next != null && !next.equals(current)) {
+ next.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ }
+
+ /**
+ * Gets the an {@link AccessibilityServiceInfo} describing this
+ * {@link AccessibilityService}. This method is useful if one wants
+ * to change some of the dynamically configurable properties at
+ * runtime.
+ *
+ * @return The accessibility service info.
+ *
+ * @see AccessibilityNodeInfo
+ */
+ public final AccessibilityServiceInfo getServiceInfo() {
+ IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getServiceInfo();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
+ }
+ }
+ return null;
+ }
+
+ /**
* Sets the {@link AccessibilityServiceInfo} that describes this service.
* <p>
* Note: You can call this method any time but the info will be picked up after
@@ -287,19 +448,33 @@
if (mInfo != null && connection != null) {
try {
connection.setServiceInfo(mInfo);
+ mInfo = null;
+ AccessibilityInteractionClient.getInstance().clearCache();
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
}
}
}
+ @Override
+ public void onCreate() {
+ Locale locale = getResources().getConfiguration().locale;
+ mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ super.onConfigurationChanged(configuration);
+ mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(configuration.locale);
+ }
+
/**
* Implement to return the implementation of the internal accessibility
* service interface.
*/
@Override
public final IBinder onBind(Intent intent) {
- return new IEventListenerWrapper(this, getMainLooper(), new Callbacks() {
+ return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
@Override
public void onServiceConnected() {
AccessibilityService.this.onServiceConnected();
@@ -319,14 +494,19 @@
public void onSetConnectionId( int connectionId) {
mConnectionId = connectionId;
}
+
+ @Override
+ public void onGesture(int gestureId) {
+ AccessibilityService.this.onGesture(gestureId);
+ }
});
}
/**
- * Implements the internal {@link IEventListener} interface to convert
+ * Implements the internal {@link IAccessibilityServiceClient} interface to convert
* incoming calls to it back to calls on an {@link AccessibilityService}.
*/
- static class IEventListenerWrapper extends IEventListener.Stub
+ static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback {
static final int NO_ID = -1;
@@ -334,12 +514,14 @@
private static final int DO_SET_SET_CONNECTION = 10;
private static final int DO_ON_INTERRUPT = 20;
private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
+ private static final int DO_ON_GESTURE = 40;
private final HandlerCaller mCaller;
private final Callbacks mCallback;
- public IEventListenerWrapper(Context context, Looper looper, Callbacks callback) {
+ public IAccessibilityServiceClientWrapper(Context context, Looper looper,
+ Callbacks callback) {
mCallback = callback;
mCaller = new HandlerCaller(context, looper, this);
}
@@ -360,6 +542,11 @@
mCaller.sendMessage(message);
}
+ public void onGesture(int gestureId) {
+ Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
+ mCaller.sendMessage(message);
+ }
+
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT :
@@ -387,6 +574,10 @@
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
}
return;
+ case DO_ON_GESTURE :
+ final int gestureId = message.arg1;
+ mCallback.onGesture(gestureId);
+ return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 8e53431..e77ed9a 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -25,11 +25,13 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import org.xmlpull.v1.XmlPullParser;
@@ -101,6 +103,37 @@
public static final int DEFAULT = 0x0000001;
/**
+ * If this flag is set the system will regard views that are not important
+ * for accessibility in addition to the ones that are important for accessibility.
+ * That is, views that are marked as not important for accessibility via
+ * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as
+ * potentially important for accessibility via
+ * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
+ * that are not important for accessibility, are both reported while querying the
+ * window content and also the accessibility service will receive accessibility events
+ * from them.
+ * <p>
+ * <strong>Note:</strong> For accessibility services targeting API version
+ * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
+ * set for the system to regard views that are not important for accessibility. For
+ * accessibility services targeting API version lower than
+ * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
+ * regarded for accessibility purposes.
+ * </p>
+ * <p>
+ * Usually views not important for accessibility are layout managers that do not
+ * react to user actions, do not draw any content, and do not have any special
+ * semantics in the context of the screen content. For example, a three by three
+ * grid can be implemented as three horizontal linear layouts and one vertical,
+ * or three vertical linear layouts and one horizontal, or one grid layout, etc.
+ * In this context the actual layout mangers used to achieve the grid configuration
+ * are not important, rather it is important that there are nine evenly distributed
+ * elements.
+ * </p>
+ */
+ public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
+
+ /**
* The event types an {@link AccessibilityService} is interested in.
* <p>
* <strong>Can be dynamically set at runtime.</strong>
@@ -165,6 +198,7 @@
* <strong>Can be dynamically set at runtime.</strong>
* </p>
* @see #DEFAULT
+ * @see #INCLUDE_NOT_IMPORTANT_VIEWS
*/
public int flags;
@@ -561,6 +595,8 @@
switch (flag) {
case DEFAULT:
return "DEFAULT";
+ case INCLUDE_NOT_IMPORTANT_VIEWS:
+ return "REGARD_VIEWS_NOT_IMPORTANT_FOR_ACCESSIBILITY";
default:
return null;
}
diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
similarity index 86%
rename from core/java/android/accessibilityservice/IEventListener.aidl
rename to core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 5536b3c..588728c 100644
--- a/core/java/android/accessibilityservice/IEventListener.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -20,15 +20,17 @@
import android.view.accessibility.AccessibilityEvent;
/**
- * Top-level interface to accessibility service component (implemented in Service).
+ * Top-level interface to an accessibility service component.
*
* @hide
*/
- oneway interface IEventListener {
+ oneway interface IAccessibilityServiceClient {
void setConnection(in IAccessibilityServiceConnection connection, int connectionId);
void onAccessibilityEvent(in AccessibilityEvent event);
void onInterrupt();
+
+ void onGesture(int gestureId);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 8d17325..30da9db 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -41,13 +41,13 @@
* to start from the root.
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
+ * @param flags Additional flags.
* @param threadId The id of the calling thread.
- * @param prefetchFlags flags to guide prefetching.
* @return The current window scale, where zero means a failure.
*/
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long threadId, int prefetchFlags);
+ IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
/**
* Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text.
@@ -94,6 +94,48 @@
long threadId);
/**
+ * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the specified
+ * focus type. The search is performed in the window whose id is specified and starts from
+ * the node whose accessibility id is specified.
+ *
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id or virtual descendant id from
+ * where to start the search. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+ * to start from the root.
+ * @param focusType The type of focus to find.
+ * @param interactionId The id of the interaction for matching with the callback result.
+ * @param callback Callback which to receive the result.
+ * @param threadId The id of the calling thread.
+ * @return The current window scale, where zero means a failure.
+ */
+ float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
+
+ /**
+ * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} to take accessibility
+ * focus in the given direction. The search is performed in the window whose id is
+ * specified and starts from the node whose accessibility id is specified.
+ *
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id or virtual descendant id from
+ * where to start the search. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+ * to start from the root.
+ * @param direction The direction in which to search for focusable.
+ * @param interactionId The id of the interaction for matching with the callback result.
+ * @param callback Callback which to receive the result.
+ * @param threadId The id of the calling thread.
+ * @return The current window scale, where zero means a failure.
+ */
+ float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
+
+ /**
* Performs an accessibility action on an
* {@link android.view.accessibility.AccessibilityNodeInfo}.
*
@@ -113,4 +155,9 @@
boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
+
+ /**
+ * @return The associated accessibility service info.
+ */
+ AccessibilityServiceInfo getServiceInfo();
}
diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
index a898c3f..c840bd6 100644
--- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java
+++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
@@ -17,7 +17,7 @@
package android.accessibilityservice;
import android.accessibilityservice.AccessibilityService.Callbacks;
-import android.accessibilityservice.AccessibilityService.IEventListenerWrapper;
+import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
import android.content.Context;
import android.os.HandlerThread;
import android.os.Looper;
@@ -66,7 +66,7 @@
private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID;
- private IEventListenerWrapper mListener;
+ private IAccessibilityServiceClientWrapper mListener;
private AccessibilityEvent mLastEvent;
@@ -133,7 +133,7 @@
mHandlerThread.start();
Looper looper = mHandlerThread.getLooper();
- mListener = new IEventListenerWrapper(null, looper, new Callbacks() {
+ mListener = new IAccessibilityServiceClientWrapper(null, looper, new Callbacks() {
@Override
public void onServiceConnected() {
/* do nothing */
@@ -175,6 +175,11 @@
mLock.notifyAll();
}
}
+
+ @Override
+ public void onGesture(int gestureId) {
+ /* do nothing */
+ }
});
final IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
@@ -252,6 +257,7 @@
public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command,
Predicate<AccessibilityEvent> predicate, long timeoutMillis)
throws TimeoutException, Exception {
+ // TODO: This is broken - remove from here when finalizing this as public APIs.
synchronized (mLock) {
// Prepare to wait for an event.
mWaitingForEventDelivery = true;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index c3cceaf..423b02a 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -73,6 +73,18 @@
public static final String KEY_ANIM_START_Y = "android:animStartY";
/**
+ * Initial width of the animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth";
+
+ /**
+ * Initial height of the animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight";
+
+ /**
* Callback for when animation is started.
* @hide
*/
@@ -83,7 +95,9 @@
/** @hide */
public static final int ANIM_CUSTOM = 1;
/** @hide */
- public static final int ANIM_THUMBNAIL = 2;
+ public static final int ANIM_SCALE_UP = 2;
+ /** @hide */
+ public static final int ANIM_THUMBNAIL = 3;
private String mPackageName;
private int mAnimationType = ANIM_NONE;
@@ -92,6 +106,8 @@
private Bitmap mThumbnail;
private int mStartX;
private int mStartY;
+ private int mStartWidth;
+ private int mStartHeight;
private IRemoteCallback mAnimationStartedListener;
/**
@@ -127,6 +143,34 @@
}
/**
+ * Create an ActivityOptions specifying an animation where the new
+ * activity is scaled from a small originating area of the screen to
+ * its final full representation.
+ *
+ * @param source The View that the new activity is animating from. This
+ * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+ * @param startX The x starting location of the new activity, relative to <var>source</var>.
+ * @param startY The y starting location of the activity, relative to <var>source</var>.
+ * @param startWidth The initial width of the new activity.
+ * @param startWidth The initial height of the new activity.
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ */
+ public static ActivityOptions makeScaleUpAnimation(View source,
+ int startX, int startY, int startWidth, int startHeight) {
+ ActivityOptions opts = new ActivityOptions();
+ opts.mPackageName = source.getContext().getPackageName();
+ opts.mAnimationType = ANIM_SCALE_UP;
+ int[] pts = new int[2];
+ source.getLocationOnScreen(pts);
+ opts.mStartX = pts[0] + startX;
+ opts.mStartY = pts[1] + startY;
+ opts.mStartWidth = startWidth;
+ opts.mStartHeight = startHeight;
+ return opts;
+ }
+
+ /**
* Create an ActivityOptions specifying an animation where a thumbnail
* is scaled from a given position to the new activity window that is
* being started.
@@ -135,8 +179,8 @@
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the initial thumbnail
* of the animation.
- * @param startX The x starting location of the bitmap, in screen coordiantes.
- * @param startY The y starting location of the bitmap, in screen coordinates.
+ * @param startX The x starting location of the bitmap, relative to <var>source</var>.
+ * @param startY The y starting location of the bitmap, relative to <var>source</var>.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
@@ -154,8 +198,8 @@
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the initial thumbnail
* of the animation.
- * @param startX The x starting location of the bitmap, in screen coordiantes.
- * @param startY The y starting location of the bitmap, in screen coordinates.
+ * @param startX The x starting location of the bitmap, relative to <var>source</var>.
+ * @param startY The y starting location of the bitmap, relative to <var>source</var>.
* @param listener Optional OnAnimationStartedListener to find out when the
* requested animation has started running. If for some reason the animation
* is not executed, the callback will happen immediately.
@@ -199,6 +243,11 @@
if (mAnimationType == ANIM_CUSTOM) {
mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+ } else if (mAnimationType == ANIM_SCALE_UP) {
+ mStartX = opts.getInt(KEY_ANIM_START_X, 0);
+ mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+ mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
+ mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
} else if (mAnimationType == ANIM_THUMBNAIL) {
mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
mStartX = opts.getInt(KEY_ANIM_START_X, 0);
@@ -244,6 +293,16 @@
}
/** @hide */
+ public int getStartWidth() {
+ return mStartWidth;
+ }
+
+ /** @hide */
+ public int getStartHeight() {
+ return mStartHeight;
+ }
+
+ /** @hide */
public IRemoteCallback getOnAnimationStartListener() {
return mAnimationStartedListener;
}
@@ -281,6 +340,13 @@
mThumbnail = null;
mAnimationStartedListener = null;
break;
+ case ANIM_SCALE_UP:
+ mAnimationType = otherOptions.mAnimationType;
+ mStartX = otherOptions.mStartX;
+ mStartY = otherOptions.mStartY;
+ mStartWidth = otherOptions.mStartWidth;
+ mStartHeight = otherOptions.mStartHeight;
+ break;
case ANIM_THUMBNAIL:
mAnimationType = otherOptions.mAnimationType;
mThumbnail = otherOptions.mThumbnail;
@@ -316,6 +382,13 @@
b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
break;
+ case ANIM_SCALE_UP:
+ b.putInt(KEY_ANIM_TYPE, mAnimationType);
+ b.putInt(KEY_ANIM_START_X, mStartX);
+ b.putInt(KEY_ANIM_START_Y, mStartY);
+ b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
+ b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
+ break;
case ANIM_THUMBNAIL:
b.putInt(KEY_ANIM_TYPE, mAnimationType);
b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
@@ -323,6 +396,7 @@
b.putInt(KEY_ANIM_START_Y, mStartY);
b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
!= null ? mAnimationStartedListener.asBinder() : null);
+ break;
}
return b;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 138a88f..8942135 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -45,6 +45,7 @@
import android.hardware.ISerialManager;
import android.hardware.SensorManager;
import android.hardware.SerialManager;
+import android.hardware.SystemSensorManager;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.usb.IUsbManager;
@@ -82,7 +83,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserId;
-import android.os.Vibrator;
+import android.os.SystemVibrator;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
@@ -407,7 +408,7 @@
registerService(SENSOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return new SensorManager(ctx.mMainThread.getHandler().getLooper());
+ return new SystemSensorManager(ctx.mMainThread.getHandler().getLooper());
}});
registerService(STATUS_BAR_SERVICE, new ServiceFetcher() {
@@ -455,7 +456,7 @@
registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return new Vibrator();
+ return new SystemVibrator();
}});
registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index dd58397..55f29e6 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -349,6 +349,7 @@
private String mMimeType;
private boolean mRoamingAllowed = true;
private int mAllowedNetworkTypes = ~0; // default to all network types allowed
+ private boolean mAllowedOverMetered = true;
private boolean mIsVisibleInDownloadsUi = true;
private boolean mScannable = false;
private boolean mUseSystemCache = false;
@@ -609,8 +610,11 @@
}
/**
- * Restrict the types of networks over which this download may proceed. By default, all
- * network types are allowed.
+ * Restrict the types of networks over which this download may proceed.
+ * By default, all network types are allowed. Consider using
+ * {@link #setAllowedOverMetered(boolean)} instead, since it's more
+ * flexible.
+ *
* @param flags any combination of the NETWORK_* bit flags.
* @return this object
*/
@@ -620,6 +624,17 @@
}
/**
+ * Set whether this download may proceed over a metered network
+ * connection. By default, metered networks are allowed.
+ *
+ * @see ConnectivityManager#isActiveNetworkMetered()
+ */
+ public Request setAllowedOverMetered(boolean allow) {
+ mAllowedOverMetered = allow;
+ return this;
+ }
+
+ /**
* Set whether this download may proceed over a roaming connection. By default, roaming is
* allowed.
* @param allowed whether to allow a roaming connection to be used
@@ -672,6 +687,7 @@
putIfNonNull(values, Downloads.Impl.COLUMN_DESCRIPTION, mDescription);
putIfNonNull(values, Downloads.Impl.COLUMN_MIME_TYPE, mMimeType);
+ // TODO: add COLUMN_ALLOW_METERED and persist
values.put(Downloads.Impl.COLUMN_VISIBILITY, mNotificationVisibility);
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 04c64a0..5cce25f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -196,10 +196,9 @@
public RemoteViews intruderView;
/**
- * A larger version of {@link #contentView}, giving the Notification an
+ * A large-format version of {@link #contentView}, giving the Notification an
* opportunity to show more detail. The system UI may choose to show this
* instead of the normal content view at its discretion.
- * @hide
*/
public RemoteViews bigContentView;
@@ -987,8 +986,6 @@
}
/**
- * @hide
- *
* Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp.
*
* @see Notification#when
@@ -1609,23 +1606,21 @@
}
/**
- * @hide because this API is still very rough
+ * Helper class for generating large-format notifications that include a large image attachment.
*
- * This is a "rebuilder": It consumes a Builder object and modifies its output.
- *
- * This represents the "big picture" style notification, with a large Bitmap atop the usual notification.
- *
- * Usage:
+ * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
* <pre class="prettyprint">
* Notification noti = new Notification.BigPictureStyle(
* new Notification.Builder()
- * .setContentTitle("New mail from " + sender.toString())
+ * .setContentTitle("New photo from " + sender.toString())
* .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
+ * .setSmallIcon(R.drawable.new_post)
* .setLargeIcon(aBitmap))
* .bigPicture(aBigBitmap)
* .build();
* </pre>
+ *
+ * @see Notification#bigContentView
*/
public static class BigPictureStyle {
private Builder mBuilder;
@@ -1656,13 +1651,9 @@
}
/**
- * @hide because this API is still very rough
+ * Helper class for generating large-format notifications that include a lot of text.
*
- * This is a "rebuilder": It consumes a Builder object and modifies its output.
- *
- * This represents the "big text" style notification, with more area for the main content text to be read in its entirety.
- *
- * Usage:
+ * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
* <pre class="prettyprint">
* Notification noti = new Notification.BigPictureStyle(
* new Notification.Builder()
@@ -1673,6 +1664,8 @@
* .bigText(aVeryLongString)
* .build();
* </pre>
+ *
+ * @see Notification#bigContentView
*/
public static class BigTextStyle {
private Builder mBuilder;
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index a655dd4..1866830 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,7 +21,12 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.TextUtils;
+import android.text.style.URLSpan;
import android.util.Log;
import java.io.FileInputStream;
@@ -144,6 +149,8 @@
public class ClipData implements Parcelable {
static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN };
+ static final String[] MIMETYPES_TEXT_HTML = new String[] {
+ ClipDescription.MIMETYPE_TEXT_HTML };
static final String[] MIMETYPES_TEXT_URILIST = new String[] {
ClipDescription.MIMETYPE_TEXT_URILIST };
static final String[] MIMETYPES_TEXT_INTENT = new String[] {
@@ -176,6 +183,7 @@
*/
public static class Item {
final CharSequence mText;
+ final String mHtmlText;
final Intent mIntent;
final Uri mUri;
@@ -184,6 +192,20 @@
*/
public Item(CharSequence text) {
mText = text;
+ mHtmlText = null;
+ mIntent = null;
+ mUri = null;
+ }
+
+ /**
+ * Create an Item consisting of a single block of (possibly styled) text,
+ * with an alternative HTML formatted representation. You <em>must</em>
+ * supply a plain text representation in addition to HTML text; coercion
+ * will not be done from HTML formated text into plain text.
+ */
+ public Item(CharSequence text, String htmlText) {
+ mText = text;
+ mHtmlText = htmlText;
mIntent = null;
mUri = null;
}
@@ -193,6 +215,7 @@
*/
public Item(Intent intent) {
mText = null;
+ mHtmlText = null;
mIntent = intent;
mUri = null;
}
@@ -202,16 +225,35 @@
*/
public Item(Uri uri) {
mText = null;
+ mHtmlText = null;
mIntent = null;
mUri = uri;
}
/**
* Create a complex Item, containing multiple representations of
- * text, intent, and/or URI.
+ * text, Intent, and/or URI.
*/
public Item(CharSequence text, Intent intent, Uri uri) {
mText = text;
+ mHtmlText = null;
+ mIntent = intent;
+ mUri = uri;
+ }
+
+ /**
+ * Create a complex Item, containing multiple representations of
+ * text, HTML text, Intent, and/or URI. If providing HTML text, you
+ * <em>must</em> supply a plain text representation as well; coercion
+ * will not be done from HTML formated text into plain text.
+ */
+ public Item(CharSequence text, String htmlText, Intent intent, Uri uri) {
+ if (htmlText != null && text == null) {
+ throw new IllegalArgumentException(
+ "Plain text must be supplied if HTML text is supplied");
+ }
+ mText = text;
+ mHtmlText = htmlText;
mIntent = intent;
mUri = uri;
}
@@ -224,6 +266,13 @@
}
/**
+ * Retrieve the raw HTML text contained in this Item.
+ */
+ public String getHtmlText() {
+ return mHtmlText;
+ }
+
+ /**
* Retrieve the raw Intent contained in this Item.
*/
public Intent getIntent() {
@@ -250,7 +299,7 @@
* the content provider does not supply a text representation, return
* the raw URI as a string.
* <li> If {@link #getIntent} is non-null, convert that to an intent:
- * URI and returnit.
+ * URI and return it.
* <li> Otherwise, return an empty string.
* </ul>
*
@@ -261,12 +310,14 @@
//BEGIN_INCLUDE(coerceToText)
public CharSequence coerceToText(Context context) {
// If this Item has an explicit textual value, simply return that.
- if (mText != null) {
- return mText;
+ CharSequence text = getText();
+ if (text != null) {
+ return text;
}
// If this Item has a URI value, try using that.
- if (mUri != null) {
+ Uri uri = getUri();
+ if (uri != null) {
// First see if the URI can be opened as a plain text stream
// (of any sub-type). If so, this is the best textual
@@ -275,7 +326,7 @@
try {
// Ask for a stream of the desired type.
AssetFileDescriptor descr = context.getContentResolver()
- .openTypedAssetFileDescriptor(mUri, "text/*", null);
+ .openTypedAssetFileDescriptor(uri, "text/*", null);
stream = descr.createInputStream();
InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
@@ -308,13 +359,14 @@
// If we couldn't open the URI as a stream, then the URI itself
// probably serves fairly well as a textual representation.
- return mUri.toString();
+ return uri.toString();
}
// Finally, if all we have is an Intent, then we can just turn that
// into text. Not the most user-friendly thing, but it's something.
- if (mIntent != null) {
- return mIntent.toUri(Intent.URI_INTENT_SCHEME);
+ Intent intent = getIntent();
+ if (intent != null) {
+ return intent.toUri(Intent.URI_INTENT_SCHEME);
}
// Shouldn't get here, but just in case...
@@ -322,6 +374,210 @@
}
//END_INCLUDE(coerceToText)
+ /**
+ * Like {@link #coerceToHtmlText(Context)}, but any text that would
+ * be returned as HTML formatting will be returned as text with
+ * style spans.
+ * @param context The caller's Context, from which its ContentResolver
+ * and other things can be retrieved.
+ * @return Returns the item's textual representation.
+ */
+ public CharSequence coerceToStyledText(Context context) {
+ CharSequence text = getText();
+ if (text instanceof Spanned) {
+ return text;
+ }
+ String htmlText = getHtmlText();
+ if (htmlText != null) {
+ try {
+ CharSequence newText = Html.fromHtml(htmlText);
+ if (newText != null) {
+ return newText;
+ }
+ } catch (RuntimeException e) {
+ // If anything bad happens, we'll fall back on the plain text.
+ }
+ }
+
+ if (text != null) {
+ return text;
+ }
+ return coerceToHtmlOrStyledText(context, true);
+ }
+
+ /**
+ * Turn this item into HTML text, regardless of the type of data it
+ * actually contains.
+ *
+ * <p>The algorithm for deciding what text to return is:
+ * <ul>
+ * <li> If {@link #getHtmlText} is non-null, return that.
+ * <li> If {@link #getText} is non-null, return that, converting to
+ * valid HTML text. If this text contains style spans,
+ * {@link Html#toHtml(Spanned) Html.toHtml(Spanned)} is used to
+ * convert them to HTML formatting.
+ * <li> If {@link #getUri} is non-null, try to retrieve its data
+ * as a text stream from its content provider. If the provider can
+ * supply text/html data, that will be preferred and returned as-is.
+ * Otherwise, any text/* data will be returned and escaped to HTML.
+ * If it is not a content: URI or the content provider does not supply
+ * a text representation, HTML text containing a link to the URI
+ * will be returned.
+ * <li> If {@link #getIntent} is non-null, convert that to an intent:
+ * URI and return as an HTML link.
+ * <li> Otherwise, return an empty string.
+ * </ul>
+ *
+ * @param context The caller's Context, from which its ContentResolver
+ * and other things can be retrieved.
+ * @return Returns the item's representation as HTML text.
+ */
+ public String coerceToHtmlText(Context context) {
+ // If the item has an explicit HTML value, simply return that.
+ String htmlText = getHtmlText();
+ if (htmlText != null) {
+ return htmlText;
+ }
+
+ // If this Item has a plain text value, return it as HTML.
+ CharSequence text = getText();
+ if (text != null) {
+ if (text instanceof Spanned) {
+ return Html.toHtml((Spanned)text);
+ }
+ return Html.escapeHtml(text);
+ }
+
+ text = coerceToHtmlOrStyledText(context, false);
+ return text != null ? text.toString() : null;
+ }
+
+ private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
+ // If this Item has a URI value, try using that.
+ if (mUri != null) {
+
+ // Check to see what data representations the content
+ // provider supports. We would like HTML text, but if that
+ // is not possible we'll live with plan text.
+ String[] types = context.getContentResolver().getStreamTypes(mUri, "text/*");
+ boolean hasHtml = false;
+ boolean hasText = false;
+ if (types != null) {
+ for (String type : types) {
+ if ("text/html".equals(type)) {
+ hasHtml = true;
+ } else if (type.startsWith("text/")) {
+ hasText = true;
+ }
+ }
+ }
+
+ // If the provider can serve data we can use, open and load it.
+ if (hasHtml || hasText) {
+ FileInputStream stream = null;
+ try {
+ // Ask for a stream of the desired type.
+ AssetFileDescriptor descr = context.getContentResolver()
+ .openTypedAssetFileDescriptor(mUri,
+ hasHtml ? "text/html" : "text/plain", null);
+ stream = descr.createInputStream();
+ InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+ // Got it... copy the stream into a local string and return it.
+ StringBuilder builder = new StringBuilder(128);
+ char[] buffer = new char[8192];
+ int len;
+ while ((len=reader.read(buffer)) > 0) {
+ builder.append(buffer, 0, len);
+ }
+ String text = builder.toString();
+ if (hasHtml) {
+ if (styled) {
+ // We loaded HTML formatted text and the caller
+ // want styled text, convert it.
+ try {
+ CharSequence newText = Html.fromHtml(text);
+ return newText != null ? newText : text;
+ } catch (RuntimeException e) {
+ return text;
+ }
+ } else {
+ // We loaded HTML formatted text and that is what
+ // the caller wants, just return it.
+ return text.toString();
+ }
+ }
+ if (styled) {
+ // We loaded plain text and the caller wants styled
+ // text, that is all we have so return it.
+ return text;
+ } else {
+ // We loaded plain text and the caller wants HTML
+ // text, escape it for HTML.
+ return Html.escapeHtml(text);
+ }
+
+ } catch (FileNotFoundException e) {
+ // Unable to open content URI as text... not really an
+ // error, just something to ignore.
+
+ } catch (IOException e) {
+ // Something bad has happened.
+ Log.w("ClippedData", "Failure loading text", e);
+ return Html.escapeHtml(e.toString());
+
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ // If we couldn't open the URI as a stream, then we can build
+ // some HTML text with the URI itself.
+ // probably serves fairly well as a textual representation.
+ if (styled) {
+ return uriToStyledText(mUri.toString());
+ } else {
+ return uriToHtml(mUri.toString());
+ }
+ }
+
+ // Finally, if all we have is an Intent, then we can just turn that
+ // into text. Not the most user-friendly thing, but it's something.
+ if (mIntent != null) {
+ if (styled) {
+ return uriToStyledText(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+ } else {
+ return uriToHtml(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+ }
+ }
+
+ // Shouldn't get here, but just in case...
+ return "";
+ }
+
+ private String uriToHtml(String uri) {
+ StringBuilder builder = new StringBuilder(256);
+ builder.append("<a href=\"");
+ builder.append(uri);
+ builder.append("\">");
+ builder.append(Html.escapeHtml(uri));
+ builder.append("</a>");
+ return builder.toString();
+ }
+
+ private CharSequence uriToStyledText(String uri) {
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(uri);
+ builder.setSpan(new URLSpan(uri), 0, builder.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return builder;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
@@ -335,7 +591,10 @@
/** @hide */
public void toShortString(StringBuilder b) {
- if (mText != null) {
+ if (mHtmlText != null) {
+ b.append("H:");
+ b.append(mHtmlText);
+ } else if (mText != null) {
b.append("T:");
b.append(mText);
} else if (mUri != null) {
@@ -409,6 +668,22 @@
}
/**
+ * Create a new ClipData holding data of the type
+ * {@link ClipDescription#MIMETYPE_TEXT_HTML}.
+ *
+ * @param label User-visible label for the clip data.
+ * @param text The text of clip as plain text, for receivers that don't
+ * handle HTML. This is required.
+ * @param htmlText The actual HTML text in the clip.
+ * @return Returns a new ClipData containing the specified data.
+ */
+ static public ClipData newHtmlText(CharSequence label, CharSequence text,
+ String htmlText) {
+ Item item = new Item(text, htmlText);
+ return new ClipData(label, MIMETYPES_TEXT_HTML, item);
+ }
+
+ /**
* Create a new ClipData holding an Intent with MIME type
* {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
*
@@ -574,6 +849,7 @@
for (int i=0; i<N; i++) {
Item item = mItems.get(i);
TextUtils.writeToParcel(item.mText, dest, flags);
+ dest.writeString(item.mHtmlText);
if (item.mIntent != null) {
dest.writeInt(1);
item.mIntent.writeToParcel(dest, flags);
@@ -600,9 +876,10 @@
final int N = in.readInt();
for (int i=0; i<N; i++) {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ String htmlText = in.readString();
Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
- mItems.add(new Item(text, intent, uri));
+ mItems.add(new Item(text, htmlText, intent, uri));
}
}
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index c6b51ef..5cb6e77 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -41,6 +41,11 @@
public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
/**
+ * The MIME type for a clip holding HTML text.
+ */
+ public static final String MIMETYPE_TEXT_HTML = "text/html";
+
+ /**
* The MIME type for a clip holding one or more URIs. This should be
* used for URIs that are meaningful to a user (such as an http: URI).
* It should <em>not</em> be used for a content: URI that references some
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2930998..722fdc6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -248,7 +248,7 @@
* @param mimeTypeFilter The desired MIME type. This may be a pattern,
* such as *\/*, to query for all available MIME types that match the
* pattern.
- * @return Returns an array of MIME type strings for all availablle
+ * @return Returns an array of MIME type strings for all available
* data streams that match the given mimeTypeFilter. If there are none,
* null is returned.
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 18d682d..6653336 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -954,7 +954,18 @@
* using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it
* should be the MIME type of the data in EXTRA_STREAM. Use {@literal *}/*
* if the MIME type is unknown (this will only allow senders that can
- * handle generic data streams).
+ * handle generic data streams). If using {@link #EXTRA_TEXT}, you can
+ * also optionally supply {@link #EXTRA_HTML_TEXT} for clients to retrieve
+ * your text with HTML formatting.
+ * <p>
+ * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+ * being sent can be supplied through {@link #setClipData(ClipData)}. This
+ * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+ * content: URIs and other advanced features of {@link ClipData}. If
+ * using this approach, you still must supply the same data through the
+ * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+ * for compatibility with old applications. If you don't set a ClipData,
+ * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
* <p>
* Optional standard extras, which may be interpreted by some recipients as
* appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
@@ -967,11 +978,13 @@
/**
* Activity Action: Deliver multiple data to someone else.
* <p>
- * Like ACTION_SEND, except the data is multiple.
+ * Like {@link #ACTION_SEND}, except the data is multiple.
* <p>
* Input: {@link #getType} is the MIME type of the data being sent.
* get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link
- * #EXTRA_STREAM} field, containing the data to be sent.
+ * #EXTRA_STREAM} field, containing the data to be sent. If using
+ * {@link #EXTRA_TEXT}, you can also optionally supply {@link #EXTRA_HTML_TEXT}
+ * for clients to retrieve your text with HTML formatting.
* <p>
* Multiple types are supported, and receivers should handle mixed types
* whenever possible. The right way for the receiver to check them is to
@@ -983,6 +996,15 @@
* be image/jpg, but if you are sending image/jpg and image/png, then the
* intent's type should be image/*.
* <p>
+ * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+ * being sent can be supplied through {@link #setClipData(ClipData)}. This
+ * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+ * content: URIs and other advanced features of {@link ClipData}. If
+ * using this approach, you still must supply the same data through the
+ * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+ * for compatibility with old applications. If you don't set a ClipData,
+ * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
+ * <p>
* Optional standard extras, which may be interpreted by some recipients as
* appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
* {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
@@ -2501,6 +2523,14 @@
public static final String EXTRA_TEXT = "android.intent.extra.TEXT";
/**
+ * A constant String that is associated with the Intent, used with
+ * {@link #ACTION_SEND} to supply an alternative to {@link #EXTRA_TEXT}
+ * as HTML formatted text. Note that you <em>must</em> also supply
+ * {@link #EXTRA_TEXT}.
+ */
+ public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+
+ /**
* A content: URI holding a stream of data associated with the Intent,
* used with {@link #ACTION_SEND} to supply the data being sent.
*/
@@ -6546,35 +6576,54 @@
final String action = getAction();
if (ACTION_SEND.equals(action)) {
- final Uri stream;
+ Uri stream = null;
try {
stream = getParcelableExtra(EXTRA_STREAM);
} catch (ClassCastException e) {
- return;
}
- if (stream != null) {
+ final CharSequence text = getCharSequenceExtra(EXTRA_TEXT);
+ final String htmlText = getStringExtra(EXTRA_HTML_TEXT);
+ if (stream != null || text != null || htmlText != null) {
final ClipData clipData = new ClipData(
- null, new String[] { getType() }, new ClipData.Item(stream));
-
+ null, new String[] { getType() },
+ new ClipData.Item(text, htmlText, null, stream));
setClipData(clipData);
addFlags(FLAG_GRANT_READ_URI_PERMISSION);
}
} else if (ACTION_SEND_MULTIPLE.equals(action)) {
- final ArrayList<Uri> streams;
+ ArrayList<Uri> streams = null;
try {
streams = getParcelableArrayListExtra(EXTRA_STREAM);
} catch (ClassCastException e) {
- return;
}
- if (streams != null && streams.size() > 0) {
- final Uri firstStream = streams.get(0);
+ final ArrayList<CharSequence> texts = getCharSequenceArrayListExtra(EXTRA_TEXT);
+ final ArrayList<String> htmlTexts = getStringArrayListExtra(EXTRA_HTML_TEXT);
+ int num = -1;
+ if (streams != null) {
+ num = streams.size();
+ }
+ if (texts != null) {
+ if (num >= 0 && num != texts.size()) {
+ // Wha...! F- you.
+ return;
+ }
+ num = texts.size();
+ }
+ if (htmlTexts != null) {
+ if (num >= 0 && num != htmlTexts.size()) {
+ // Wha...! F- you.
+ return;
+ }
+ num = htmlTexts.size();
+ }
+ if (num > 0) {
final ClipData clipData = new ClipData(
- null, new String[] { getType() }, new ClipData.Item(firstStream));
+ null, new String[] { getType() },
+ makeClipItem(streams, texts, htmlTexts, 0));
- final int size = streams.size();
- for (int i = 1; i < size; i++) {
- clipData.addItem(new ClipData.Item(streams.get(i)));
+ for (int i = 1; i < num; i++) {
+ clipData.addItem(makeClipItem(streams, texts, htmlTexts, i));
}
setClipData(clipData);
@@ -6582,4 +6631,12 @@
}
}
}
+
+ private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts,
+ ArrayList<String> htmlTexts, int which) {
+ Uri uri = streams != null ? streams.get(which) : null;
+ CharSequence text = texts != null ? texts.get(which) : null;
+ String htmlText = htmlTexts != null ? htmlTexts.get(which) : null;
+ return new ClipData.Item(text, htmlText, null, uri);
+ }
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 56fd5f8..9b8454a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -373,6 +373,6 @@
List<UserInfo> getUsers();
UserInfo getUser(int userId);
- void setPermissionEnforcement(String permission, int enforcement);
- int getPermissionEnforcement(String permission);
+ void setPermissionEnforced(String permission, boolean enforced);
+ boolean isPermissionEnforced(String permission);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5d890d4..675f77e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.util.AndroidException;
import android.util.DisplayMetrics;
@@ -1091,21 +1092,7 @@
= "android.content.pm.extra.VERIFICATION_INSTALL_FLAGS";
/** {@hide} */
- public static final int ENFORCEMENT_DEFAULT = 0;
- /** {@hide} */
- public static final int ENFORCEMENT_YES = 1;
-
- /** {@hide} */
- public static String enforcementToString(int enforcement) {
- switch (enforcement) {
- case ENFORCEMENT_DEFAULT:
- return "DEFAULT";
- case ENFORCEMENT_YES:
- return "YES";
- default:
- return Integer.toString(enforcement);
- }
- }
+ public static final boolean DEFAULT_ENFORCE_READ_EXTERNAL_STORAGE = !"user".equals(Build.TYPE);
/**
* Retrieve overall information about an application package that is
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 254f652..04f6377 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -711,6 +711,7 @@
throw new IllegalArgumentException("sql must not be null.");
}
+ int changedRows = 0;
final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
sql, bindArgs);
try {
@@ -721,8 +722,9 @@
applyBlockGuardPolicy(statement);
attachCancellationSignal(cancellationSignal);
try {
- return nativeExecuteForChangedRowCount(
+ changedRows = nativeExecuteForChangedRowCount(
mConnectionPtr, statement.mStatementPtr);
+ return changedRows;
} finally {
detachCancellationSignal(cancellationSignal);
}
@@ -733,7 +735,9 @@
mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation(cookie);
+ if (mRecentOperations.endOperationDeferLog(cookie)) {
+ mRecentOperations.logOperation(cookie, "changedRows=" + changedRows);
+ }
}
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 83b6986..640b47b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -950,11 +950,10 @@
/**
* Callback interface used to notify on auto focus start and stop.
*
- * <p>This is useful for continuous autofocus -- {@link Parameters#FOCUS_MODE_CONTINUOUS_VIDEO}
- * and {@link Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}. Applications can
- * show autofocus animation.</p>
- *
- * @hide
+ * <p>This is only supported in continuous autofocus modes -- {@link
+ * Parameters#FOCUS_MODE_CONTINUOUS_VIDEO} and {@link
+ * Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}. Applications can show
+ * autofocus animation based on this.</p>
*/
public interface AutoFocusMoveCallback
{
@@ -962,7 +961,7 @@
* Called when the camera auto focus starts or stops.
*
* @param start true if focus starts to move, false if focus stops to move
- * @param camera the Camera service object
+ * @param camera the Camera service object
*/
void onAutoFocusMoving(boolean start, Camera camera);
}
@@ -971,7 +970,6 @@
* Sets camera auto-focus move callback.
*
* @param cb the callback to run
- * @hide
*/
public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) {
mAutoFocusMoveCallback = cb;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 63fb32d..3c70dc6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -131,7 +131,6 @@
private float mResolution;
private float mPower;
private int mMinDelay;
- private int mLegacyType;
Sensor() {
@@ -203,12 +202,4 @@
mMaxRange = max;
mResolution = res;
}
-
- void setLegacyType(int legacyType) {
- mLegacyType = legacyType;
- }
-
- int getLegacyType() {
- return mLegacyType;
- }
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 3fdf246..d783db7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,16 +16,10 @@
package android.hardware;
-import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.os.Handler;
-import android.os.Message;
import android.os.ServiceManager;
-import android.util.Log;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.Surface;
@@ -83,11 +77,21 @@
* @see Sensor
*
*/
-public class SensorManager
-{
- private static final String TAG = "SensorManager";
+public abstract class SensorManager {
private static final float[] mTempMatrix = new float[16];
+ private static boolean sInitialized;
+ private static IWindowManager sWindowManager;
+ private static int sRotation = Surface.ROTATION_0;
+
+ // List of legacy listeners. Guarded by mLegacyListenersMap.
+ private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
+ new HashMap<SensorListener, LegacyListener>();
+
+ // Cached lists of sensors by type. Guarded by mSensorListByType.
+ private final SparseArray<List<Sensor>> mSensorListByType =
+ new SparseArray<List<Sensor>>();
+
/* NOTE: sensor IDs must be a power of 2 */
/**
@@ -353,287 +357,13 @@
/** see {@link #remapCoordinateSystem} */
public static final int AXIS_MINUS_Z = AXIS_Z | 0x80;
- /*-----------------------------------------------------------------------*/
-
- Looper mMainLooper;
- @SuppressWarnings("deprecation")
- private HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
- new HashMap<SensorListener, LegacyListener>();
-
- /*-----------------------------------------------------------------------*/
-
- private static final int SENSOR_DISABLE = -1;
- private static boolean sSensorModuleInitialized = false;
- private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
- private static SparseArray<List<Sensor>> sSensorListByType = new SparseArray<List<Sensor>>();
- private static IWindowManager sWindowManager;
- private static int sRotation = Surface.ROTATION_0;
- /* The thread and the sensor list are global to the process
- * but the actual thread is spawned on demand */
- private static SensorThread sSensorThread;
- private static int sQueue;
-
- // Used within this module from outside SensorManager, don't make private
- static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
- static final ArrayList<ListenerDelegate> sListeners =
- new ArrayList<ListenerDelegate>();
-
- /*-----------------------------------------------------------------------*/
-
- private class SensorEventPool {
- private final int mPoolSize;
- private final SensorEvent mPool[];
- private int mNumItemsInPool;
-
- private SensorEvent createSensorEvent() {
- // maximal size for all legacy events is 3
- return new SensorEvent(3);
- }
-
- SensorEventPool(int poolSize) {
- mPoolSize = poolSize;
- mNumItemsInPool = poolSize;
- mPool = new SensorEvent[poolSize];
- }
-
- SensorEvent getFromPool() {
- SensorEvent t = null;
- synchronized (this) {
- if (mNumItemsInPool > 0) {
- // remove the "top" item from the pool
- final int index = mPoolSize - mNumItemsInPool;
- t = mPool[index];
- mPool[index] = null;
- mNumItemsInPool--;
- }
- }
- if (t == null) {
- // the pool was empty or this item was removed from the pool for
- // the first time. In any case, we need to create a new item.
- t = createSensorEvent();
- }
- return t;
- }
-
- void returnToPool(SensorEvent t) {
- synchronized (this) {
- // is there space left in the pool?
- if (mNumItemsInPool < mPoolSize) {
- // if so, return the item to the pool
- mNumItemsInPool++;
- final int index = mPoolSize - mNumItemsInPool;
- mPool[index] = t;
- }
- }
- }
- }
-
- private static SensorEventPool sPool;
-
- /*-----------------------------------------------------------------------*/
-
- static private class SensorThread {
-
- Thread mThread;
- boolean mSensorsReady;
-
- SensorThread() {
- }
-
- @Override
- protected void finalize() {
- }
-
- // must be called with sListeners lock
- boolean startLocked() {
- try {
- if (mThread == null) {
- mSensorsReady = false;
- SensorThreadRunnable runnable = new SensorThreadRunnable();
- Thread thread = new Thread(runnable, SensorThread.class.getName());
- thread.start();
- synchronized (runnable) {
- while (mSensorsReady == false) {
- runnable.wait();
- }
- }
- mThread = thread;
- }
- } catch (InterruptedException e) {
- }
- return mThread == null ? false : true;
- }
-
- private class SensorThreadRunnable implements Runnable {
- SensorThreadRunnable() {
- }
-
- private boolean open() {
- // NOTE: this cannot synchronize on sListeners, since
- // it's held in the main thread at least until we
- // return from here.
- sQueue = sensors_create_queue();
- return true;
- }
-
- public void run() {
- //Log.d(TAG, "entering main sensor thread");
- final float[] values = new float[3];
- final int[] status = new int[1];
- final long timestamp[] = new long[1];
- Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- if (!open()) {
- return;
- }
-
- synchronized (this) {
- // we've open the driver, we're ready to open the sensors
- mSensorsReady = true;
- this.notify();
- }
-
- while (true) {
- // wait for an event
- final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
-
- int accuracy = status[0];
- synchronized (sListeners) {
- if (sensor == -1 || sListeners.isEmpty()) {
- // we lost the connection to the event stream. this happens
- // when the last listener is removed or if there is an error
- if (sensor == -1 && !sListeners.isEmpty()) {
- // log a warning in case of abnormal termination
- Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
- }
- // we have no more listeners or polling failed, terminate the thread
- sensors_destroy_queue(sQueue);
- sQueue = 0;
- mThread = null;
- break;
- }
- final Sensor sensorObject = sHandleToSensor.get(sensor);
- if (sensorObject != null) {
- // report the sensor event to all listeners that
- // care about it.
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate listener = sListeners.get(i);
- if (listener.hasSensor(sensorObject)) {
- // this is asynchronous (okay to call
- // with sListeners lock held).
- listener.onSensorChangedLocked(sensorObject,
- values, timestamp, accuracy);
- }
- }
- }
- }
- }
- //Log.d(TAG, "exiting main sensor thread");
- }
- }
- }
-
- /*-----------------------------------------------------------------------*/
-
- private class ListenerDelegate {
- private final SensorEventListener mSensorEventListener;
- private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
- private final Handler mHandler;
- public SparseBooleanArray mSensors = new SparseBooleanArray();
- public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
- public SparseIntArray mSensorAccuracies = new SparseIntArray();
-
- ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
- mSensorEventListener = listener;
- Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- // currently we create one Handler instance per listener, but we could
- // have one per looper (we'd need to pass the ListenerDelegate
- // instance to handleMessage and keep track of them separately).
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- final SensorEvent t = (SensorEvent)msg.obj;
- final int handle = t.sensor.getHandle();
-
- switch (t.sensor.getType()) {
- // Only report accuracy for sensors that support it.
- case Sensor.TYPE_MAGNETIC_FIELD:
- case Sensor.TYPE_ORIENTATION:
- // call onAccuracyChanged() only if the value changes
- final int accuracy = mSensorAccuracies.get(handle);
- if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
- mSensorAccuracies.put(handle, t.accuracy);
- mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
- }
- break;
- default:
- // For other sensors, just report the accuracy once
- if (mFirstEvent.get(handle) == false) {
- mFirstEvent.put(handle, true);
- mSensorEventListener.onAccuracyChanged(
- t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
- }
- break;
- }
-
- mSensorEventListener.onSensorChanged(t);
- sPool.returnToPool(t);
- }
- };
- addSensor(sensor);
- }
-
- Object getListener() {
- return mSensorEventListener;
- }
-
- void addSensor(Sensor sensor) {
- mSensors.put(sensor.getHandle(), true);
- mSensorList.add(sensor);
- }
- int removeSensor(Sensor sensor) {
- mSensors.delete(sensor.getHandle());
- mSensorList.remove(sensor);
- return mSensors.size();
- }
- boolean hasSensor(Sensor sensor) {
- return mSensors.get(sensor.getHandle());
- }
- List<Sensor> getSensors() {
- return mSensorList;
- }
-
- void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
- SensorEvent t = sPool.getFromPool();
- final float[] v = t.values;
- v[0] = values[0];
- v[1] = values[1];
- v[2] = values[2];
- t.timestamp = timestamp[0];
- t.accuracy = accuracy;
- t.sensor = sensor;
- Message msg = Message.obtain();
- msg.what = 0;
- msg.obj = t;
- msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
- }
- }
/**
* {@hide}
*/
- public SensorManager(Looper mainLooper) {
- mMainLooper = mainLooper;
-
-
- synchronized(sListeners) {
- if (!sSensorModuleInitialized) {
- sSensorModuleInitialized = true;
-
- nativeClassInit();
-
+ public SensorManager() {
+ synchronized (SensorManager.class) {
+ if (!sInitialized) {
sWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
if (sWindowManager != null) {
@@ -650,43 +380,15 @@
} catch (RemoteException e) {
}
}
-
- // initialize the sensor list
- sensors_module_init();
- final ArrayList<Sensor> fullList = sFullSensorsList;
- int i = 0;
- do {
- Sensor sensor = new Sensor();
- i = sensors_module_get_next_sensor(sensor, i);
-
- if (i>=0) {
- //Log.d(TAG, "found sensor: " + sensor.getName() +
- // ", handle=" + sensor.getHandle());
- sensor.setLegacyType(getLegacySensorType(sensor.getType()));
- fullList.add(sensor);
- sHandleToSensor.append(sensor.getHandle(), sensor);
- }
- } while (i>0);
-
- sPool = new SensorEventPool( sFullSensorsList.size()*2 );
- sSensorThread = new SensorThread();
}
}
}
- private int getLegacySensorType(int type) {
- switch (type) {
- case Sensor.TYPE_ACCELEROMETER:
- return SENSOR_ACCELEROMETER;
- case Sensor.TYPE_MAGNETIC_FIELD:
- return SENSOR_MAGNETIC_FIELD;
- case Sensor.TYPE_ORIENTATION:
- return SENSOR_ORIENTATION_RAW;
- case Sensor.TYPE_TEMPERATURE:
- return SENSOR_TEMPERATURE;
- }
- return 0;
- }
+ /**
+ * Gets the full list of sensors that are available.
+ * @hide
+ */
+ protected abstract List<Sensor> getFullSensorList();
/**
* @return available sensors.
@@ -696,7 +398,7 @@
@Deprecated
public int getSensors() {
int result = 0;
- final ArrayList<Sensor> fullList = sFullSensorsList;
+ final List<Sensor> fullList = getFullSensorList();
for (Sensor i : fullList) {
switch (i.getType()) {
case Sensor.TYPE_ACCELEROMETER:
@@ -731,9 +433,9 @@
public List<Sensor> getSensorList(int type) {
// cache the returned lists the first time
List<Sensor> list;
- final ArrayList<Sensor> fullList = sFullSensorsList;
- synchronized(fullList) {
- list = sSensorListByType.get(type);
+ final List<Sensor> fullList = getFullSensorList();
+ synchronized (mSensorListByType) {
+ list = mSensorListByType.get(type);
if (list == null) {
if (type == Sensor.TYPE_ALL) {
list = fullList;
@@ -745,7 +447,7 @@
}
}
list = Collections.unmodifiableList(list);
- sSensorListByType.append(type, list);
+ mSensorListByType.append(type, list);
}
}
return list;
@@ -836,34 +538,37 @@
@SuppressWarnings("deprecation")
private boolean registerLegacyListener(int legacyType, int type,
- SensorListener listener, int sensors, int rate)
- {
- if (listener == null) {
- return false;
- }
+ SensorListener listener, int sensors, int rate) {
boolean result = false;
// Are we activating this legacy sensor?
if ((sensors & legacyType) != 0) {
// if so, find a suitable Sensor
Sensor sensor = getDefaultSensor(type);
if (sensor != null) {
- // If we don't already have one, create a LegacyListener
- // to wrap this listener and process the events as
- // they are expected by legacy apps.
- LegacyListener legacyListener = null;
+ // We do all of this work holding the legacy listener lock to ensure
+ // that the invariants around listeners are maintained. This is safe
+ // because neither registerLegacyListener nor unregisterLegacyListener
+ // are called reentrantly while sensors are being registered or unregistered.
synchronized (mLegacyListenersMap) {
- legacyListener = mLegacyListenersMap.get(listener);
+ // If we don't already have one, create a LegacyListener
+ // to wrap this listener and process the events as
+ // they are expected by legacy apps.
+ LegacyListener legacyListener = mLegacyListenersMap.get(listener);
if (legacyListener == null) {
// we didn't find a LegacyListener for this client,
// create one, and put it in our list.
legacyListener = new LegacyListener(listener);
mLegacyListenersMap.put(listener, legacyListener);
}
+
+ // register this legacy sensor with this legacy listener
+ if (legacyListener.registerSensor(legacyType)) {
+ // and finally, register the legacy listener with the new apis
+ result = registerListener(legacyListener, sensor, rate);
+ } else {
+ result = true; // sensor already enabled
+ }
}
- // register this legacy sensor with this legacy listener
- legacyListener.registerSensor(legacyType);
- // and finally, register the legacy listener with the new apis
- result = registerListener(legacyListener, sensor, rate);
}
}
return result;
@@ -884,6 +589,9 @@
*/
@Deprecated
public void unregisterListener(SensorListener listener, int sensors) {
+ if (listener == null) {
+ return;
+ }
unregisterLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
listener, sensors);
unregisterLegacyListener(SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
@@ -896,51 +604,6 @@
listener, sensors);
}
- @SuppressWarnings("deprecation")
- private void unregisterLegacyListener(int legacyType, int type,
- SensorListener listener, int sensors)
- {
- if (listener == null) {
- return;
- }
- // do we know about this listener?
- LegacyListener legacyListener = null;
- synchronized (mLegacyListenersMap) {
- legacyListener = mLegacyListenersMap.get(listener);
- }
- if (legacyListener != null) {
- // Are we deactivating this legacy sensor?
- if ((sensors & legacyType) != 0) {
- // if so, find the corresponding Sensor
- Sensor sensor = getDefaultSensor(type);
- if (sensor != null) {
- // unregister this legacy sensor and if we don't
- // need the corresponding Sensor, unregister it too
- if (legacyListener.unregisterSensor(legacyType)) {
- // corresponding sensor not needed, unregister
- unregisterListener(legacyListener, sensor);
- // finally check if we still need the legacyListener
- // in our mapping, if not, get rid of it too.
- synchronized(sListeners) {
- boolean found = false;
- for (ListenerDelegate i : sListeners) {
- if (i.getListener() == legacyListener) {
- found = true;
- break;
- }
- }
- if (!found) {
- synchronized (mLegacyListenersMap) {
- mLegacyListenersMap.remove(listener);
- }
- }
- }
- }
- }
- }
- }
- }
-
/**
* Unregisters a listener for all sensors.
*
@@ -956,6 +619,40 @@
unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW);
}
+ @SuppressWarnings("deprecation")
+ private void unregisterLegacyListener(int legacyType, int type,
+ SensorListener listener, int sensors) {
+ // Are we deactivating this legacy sensor?
+ if ((sensors & legacyType) != 0) {
+ // if so, find the corresponding Sensor
+ Sensor sensor = getDefaultSensor(type);
+ if (sensor != null) {
+ // We do all of this work holding the legacy listener lock to ensure
+ // that the invariants around listeners are maintained. This is safe
+ // because neither registerLegacyListener nor unregisterLegacyListener
+ // are called re-entrantly while sensors are being registered or unregistered.
+ synchronized (mLegacyListenersMap) {
+ // do we know about this listener?
+ LegacyListener legacyListener = mLegacyListenersMap.get(listener);
+ if (legacyListener != null) {
+ // unregister this legacy sensor and if we don't
+ // need the corresponding Sensor, unregister it too
+ if (legacyListener.unregisterSensor(legacyType)) {
+ // corresponding sensor not needed, unregister
+ unregisterListener(legacyListener, sensor);
+
+ // finally check if we still need the legacyListener
+ // in our mapping, if not, get rid of it too.
+ if (!legacyListener.hasSensors()) {
+ mLegacyListenersMap.remove(listener);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
/**
* Unregisters a listener for the sensors with which it is registered.
*
@@ -970,7 +667,11 @@
*
*/
public void unregisterListener(SensorEventListener listener, Sensor sensor) {
- unregisterListener((Object)listener, sensor);
+ if (listener == null || sensor == null) {
+ return;
+ }
+
+ unregisterListenerImpl(listener, sensor);
}
/**
@@ -984,9 +685,16 @@
*
*/
public void unregisterListener(SensorEventListener listener) {
- unregisterListener((Object)listener);
+ if (listener == null) {
+ return;
+ }
+
+ unregisterListenerImpl(listener, null);
}
+ /** @hide */
+ protected abstract void unregisterListenerImpl(SensorEventListener listener, Sensor sensor);
+
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
@@ -1019,31 +727,6 @@
return registerListener(listener, sensor, rate, null);
}
- private boolean enableSensorLocked(Sensor sensor, int delay) {
- boolean result = false;
- for (ListenerDelegate i : sListeners) {
- if (i.hasSensor(sensor)) {
- String name = sensor.getName();
- int handle = sensor.getHandle();
- result = sensors_enable_sensor(sQueue, name, handle, delay);
- break;
- }
- }
- return result;
- }
-
- private boolean disableSensorLocked(Sensor sensor) {
- for (ListenerDelegate i : sListeners) {
- if (i.hasSensor(sensor)) {
- // not an error, it's just that this sensor is still in use
- return true;
- }
- }
- String name = sensor.getName();
- int handle = sensor.getHandle();
- return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
- }
-
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
@@ -1081,7 +764,7 @@
if (listener == null || sensor == null) {
return false;
}
- boolean result = true;
+
int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
@@ -1101,92 +784,12 @@
break;
}
- synchronized (sListeners) {
- // look for this listener in our list
- ListenerDelegate l = null;
- for (ListenerDelegate i : sListeners) {
- if (i.getListener() == listener) {
- l = i;
- break;
- }
- }
-
- // if we don't find it, add it to the list
- if (l == null) {
- l = new ListenerDelegate(listener, sensor, handler);
- sListeners.add(l);
- // if the list is not empty, start our main thread
- if (!sListeners.isEmpty()) {
- if (sSensorThread.startLocked()) {
- if (!enableSensorLocked(sensor, delay)) {
- // oops. there was an error
- sListeners.remove(l);
- result = false;
- }
- } else {
- // there was an error, remove the listener
- sListeners.remove(l);
- result = false;
- }
- } else {
- // weird, we couldn't add the listener
- result = false;
- }
- } else {
- l.addSensor(sensor);
- if (!enableSensorLocked(sensor, delay)) {
- // oops. there was an error
- l.removeSensor(sensor);
- result = false;
- }
- }
- }
-
- return result;
+ return registerListenerImpl(listener, sensor, delay, handler);
}
- private void unregisterListener(Object listener, Sensor sensor) {
- if (listener == null || sensor == null) {
- return;
- }
-
- synchronized (sListeners) {
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate l = sListeners.get(i);
- if (l.getListener() == listener) {
- if (l.removeSensor(sensor) == 0) {
- // if we have no more sensors enabled on this listener,
- // take it off the list.
- sListeners.remove(i);
- }
- break;
- }
- }
- disableSensorLocked(sensor);
- }
- }
-
- private void unregisterListener(Object listener) {
- if (listener == null) {
- return;
- }
-
- synchronized (sListeners) {
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate l = sListeners.get(i);
- if (l.getListener() == listener) {
- sListeners.remove(i);
- // disable all sensors for this listener
- for (Sensor sensor : l.getSensors()) {
- disableSensorLocked(sensor);
- }
- break;
- }
- }
- }
- }
+ /** @hide */
+ protected abstract boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
+ int delay, Handler handler);
/**
* <p>
@@ -1653,228 +1256,11 @@
* @param p atmospheric pressure
* @return Altitude in meters
*/
- public static float getAltitude(float p0, float p) {
+ public static float getAltitude(float p0, float p) {
final float coef = 1.0f / 5.255f;
return 44330.0f * (1.0f - (float)Math.pow(p/p0, coef));
}
-
- /**
- * {@hide}
- */
- public void onRotationChanged(int rotation) {
- synchronized(sListeners) {
- sRotation = rotation;
- }
- }
-
- static int getRotation() {
- synchronized(sListeners) {
- return sRotation;
- }
- }
-
- private class LegacyListener implements SensorEventListener {
- private float mValues[] = new float[6];
- @SuppressWarnings("deprecation")
- private SensorListener mTarget;
- private int mSensors;
- private final LmsFilter mYawfilter = new LmsFilter();
-
- @SuppressWarnings("deprecation")
- LegacyListener(SensorListener target) {
- mTarget = target;
- mSensors = 0;
- }
-
- void registerSensor(int legacyType) {
- mSensors |= legacyType;
- }
-
- boolean unregisterSensor(int legacyType) {
- mSensors &= ~legacyType;
- int mask = SENSOR_ORIENTATION|SENSOR_ORIENTATION_RAW;
- if (((legacyType&mask)!=0) && ((mSensors&mask)!=0)) {
- return false;
- }
- return true;
- }
-
- @SuppressWarnings("deprecation")
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- try {
- mTarget.onAccuracyChanged(sensor.getLegacyType(), accuracy);
- } catch (AbstractMethodError e) {
- // old app that doesn't implement this method
- // just ignore it.
- }
- }
-
- @SuppressWarnings("deprecation")
- public void onSensorChanged(SensorEvent event) {
- final float v[] = mValues;
- v[0] = event.values[0];
- v[1] = event.values[1];
- v[2] = event.values[2];
- int legacyType = event.sensor.getLegacyType();
- mapSensorDataToWindow(legacyType, v, SensorManager.getRotation());
- if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
- if ((mSensors & SENSOR_ORIENTATION_RAW)!=0) {
- mTarget.onSensorChanged(SENSOR_ORIENTATION_RAW, v);
- }
- if ((mSensors & SENSOR_ORIENTATION)!=0) {
- v[0] = mYawfilter.filter(event.timestamp, v[0]);
- mTarget.onSensorChanged(SENSOR_ORIENTATION, v);
- }
- } else {
- mTarget.onSensorChanged(legacyType, v);
- }
- }
-
- /*
- * Helper function to convert the specified sensor's data to the windows's
- * coordinate space from the device's coordinate space.
- *
- * output: 3,4,5: values in the old API format
- * 0,1,2: transformed values in the old API format
- *
- */
- private void mapSensorDataToWindow(int sensor,
- float[] values, int orientation) {
- float x = values[0];
- float y = values[1];
- float z = values[2];
-
- switch (sensor) {
- case SensorManager.SENSOR_ORIENTATION:
- case SensorManager.SENSOR_ORIENTATION_RAW:
- z = -z;
- break;
- case SensorManager.SENSOR_ACCELEROMETER:
- x = -x;
- y = -y;
- z = -z;
- break;
- case SensorManager.SENSOR_MAGNETIC_FIELD:
- x = -x;
- y = -y;
- break;
- }
- values[0] = x;
- values[1] = y;
- values[2] = z;
- values[3] = x;
- values[4] = y;
- values[5] = z;
-
- if ((orientation & Surface.ROTATION_90) != 0) {
- // handles 90 and 270 rotation
- switch (sensor) {
- case SENSOR_ACCELEROMETER:
- case SENSOR_MAGNETIC_FIELD:
- values[0] =-y;
- values[1] = x;
- values[2] = z;
- break;
- case SENSOR_ORIENTATION:
- case SENSOR_ORIENTATION_RAW:
- values[0] = x + ((x < 270) ? 90 : -270);
- values[1] = z;
- values[2] = y;
- break;
- }
- }
- if ((orientation & Surface.ROTATION_180) != 0) {
- x = values[0];
- y = values[1];
- z = values[2];
- // handles 180 (flip) and 270 (flip + 90) rotation
- switch (sensor) {
- case SENSOR_ACCELEROMETER:
- case SENSOR_MAGNETIC_FIELD:
- values[0] =-x;
- values[1] =-y;
- values[2] = z;
- break;
- case SENSOR_ORIENTATION:
- case SENSOR_ORIENTATION_RAW:
- values[0] = (x >= 180) ? (x - 180) : (x + 180);
- values[1] =-y;
- values[2] =-z;
- break;
- }
- }
- }
- }
-
- class LmsFilter {
- private static final int SENSORS_RATE_MS = 20;
- private static final int COUNT = 12;
- private static final float PREDICTION_RATIO = 1.0f/3.0f;
- private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
- private float mV[] = new float[COUNT*2];
- private float mT[] = new float[COUNT*2];
- private int mIndex;
-
- public LmsFilter() {
- mIndex = COUNT;
- }
-
- public float filter(long time, float in) {
- float v = in;
- final float ns = 1.0f / 1000000000.0f;
- final float t = time*ns;
- float v1 = mV[mIndex];
- if ((v-v1) > 180) {
- v -= 360;
- } else if ((v1-v) > 180) {
- v += 360;
- }
- /* Manage the circular buffer, we write the data twice spaced
- * by COUNT values, so that we don't have to copy the array
- * when it's full
- */
- mIndex++;
- if (mIndex >= COUNT*2)
- mIndex = COUNT;
- mV[mIndex] = v;
- mT[mIndex] = t;
- mV[mIndex-COUNT] = v;
- mT[mIndex-COUNT] = t;
-
- float A, B, C, D, E;
- float a, b;
- int i;
-
- A = B = C = D = E = 0;
- for (i=0 ; i<COUNT-1 ; i++) {
- final int j = mIndex - 1 - i;
- final float Z = mV[j];
- final float T = 0.5f*(mT[j] + mT[j+1]) - t;
- float dT = mT[j] - mT[j+1];
- dT *= dT;
- A += Z*dT;
- B += T*(T*dT);
- C += (T*dT);
- D += Z*(T*dT);
- E += dT;
- }
- b = (A*B + C*D) / (E*B + C*C);
- a = (E*b - A) / C;
- float f = b + PREDICTION_TIME*a;
-
- // Normalize
- f *= (1.0f / 360.0f);
- if (((f>=0)?f:-f) >= 0.5f)
- f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
- if (f < 0)
- f += 1.0f;
- f *= 360.0f;
- return f;
- }
- }
-
-
/** Helper function to compute the angle change between two rotation matrices.
* Given a current rotation matrix (R) and a previous rotation matrix
* (prevR) computes the rotation around the x,y, and z axes which
@@ -2060,14 +1446,300 @@
Q[3] = rv[2];
}
- private static native void nativeClassInit();
+ static void onRotationChanged(int rotation) {
+ synchronized (SensorManager.class) {
+ sRotation = rotation;
+ }
+ }
- private static native int sensors_module_init();
- private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+ static int getRotation() {
+ synchronized (SensorManager.class) {
+ return sRotation;
+ }
+ }
- // Used within this module from outside SensorManager, don't make private
- static native int sensors_create_queue();
- static native void sensors_destroy_queue(int queue);
- static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
- static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
+ private static final class LegacyListener implements SensorEventListener {
+ private float mValues[] = new float[6];
+ @SuppressWarnings("deprecation")
+ private SensorListener mTarget;
+ private int mSensors;
+ private final LmsFilter mYawfilter = new LmsFilter();
+
+ @SuppressWarnings("deprecation")
+ LegacyListener(SensorListener target) {
+ mTarget = target;
+ mSensors = 0;
+ }
+
+ boolean registerSensor(int legacyType) {
+ if ((mSensors & legacyType) != 0) {
+ return false;
+ }
+ boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors);
+ mSensors |= legacyType;
+ if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) {
+ return false; // don't need to re-register the orientation sensor
+ }
+ return true;
+ }
+
+ boolean unregisterSensor(int legacyType) {
+ if ((mSensors & legacyType) == 0) {
+ return false;
+ }
+ mSensors &= ~legacyType;
+ if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) {
+ return false; // can't unregister the orientation sensor just yet
+ }
+ return true;
+ }
+
+ boolean hasSensors() {
+ return mSensors != 0;
+ }
+
+ private static boolean hasOrientationSensor(int sensors) {
+ return (sensors & (SENSOR_ORIENTATION | SENSOR_ORIENTATION_RAW)) != 0;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ try {
+ mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy);
+ } catch (AbstractMethodError e) {
+ // old app that doesn't implement this method
+ // just ignore it.
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void onSensorChanged(SensorEvent event) {
+ final float v[] = mValues;
+ v[0] = event.values[0];
+ v[1] = event.values[1];
+ v[2] = event.values[2];
+ int type = event.sensor.getType();
+ int legacyType = getLegacySensorType(type);
+ mapSensorDataToWindow(legacyType, v, SensorManager.getRotation());
+ if (type == Sensor.TYPE_ORIENTATION) {
+ if ((mSensors & SENSOR_ORIENTATION_RAW)!=0) {
+ mTarget.onSensorChanged(SENSOR_ORIENTATION_RAW, v);
+ }
+ if ((mSensors & SENSOR_ORIENTATION)!=0) {
+ v[0] = mYawfilter.filter(event.timestamp, v[0]);
+ mTarget.onSensorChanged(SENSOR_ORIENTATION, v);
+ }
+ } else {
+ mTarget.onSensorChanged(legacyType, v);
+ }
+ }
+
+ /*
+ * Helper function to convert the specified sensor's data to the windows's
+ * coordinate space from the device's coordinate space.
+ *
+ * output: 3,4,5: values in the old API format
+ * 0,1,2: transformed values in the old API format
+ *
+ */
+ private void mapSensorDataToWindow(int sensor,
+ float[] values, int orientation) {
+ float x = values[0];
+ float y = values[1];
+ float z = values[2];
+
+ switch (sensor) {
+ case SensorManager.SENSOR_ORIENTATION:
+ case SensorManager.SENSOR_ORIENTATION_RAW:
+ z = -z;
+ break;
+ case SensorManager.SENSOR_ACCELEROMETER:
+ x = -x;
+ y = -y;
+ z = -z;
+ break;
+ case SensorManager.SENSOR_MAGNETIC_FIELD:
+ x = -x;
+ y = -y;
+ break;
+ }
+ values[0] = x;
+ values[1] = y;
+ values[2] = z;
+ values[3] = x;
+ values[4] = y;
+ values[5] = z;
+
+ if ((orientation & Surface.ROTATION_90) != 0) {
+ // handles 90 and 270 rotation
+ switch (sensor) {
+ case SENSOR_ACCELEROMETER:
+ case SENSOR_MAGNETIC_FIELD:
+ values[0] =-y;
+ values[1] = x;
+ values[2] = z;
+ break;
+ case SENSOR_ORIENTATION:
+ case SENSOR_ORIENTATION_RAW:
+ values[0] = x + ((x < 270) ? 90 : -270);
+ values[1] = z;
+ values[2] = y;
+ break;
+ }
+ }
+ if ((orientation & Surface.ROTATION_180) != 0) {
+ x = values[0];
+ y = values[1];
+ z = values[2];
+ // handles 180 (flip) and 270 (flip + 90) rotation
+ switch (sensor) {
+ case SENSOR_ACCELEROMETER:
+ case SENSOR_MAGNETIC_FIELD:
+ values[0] =-x;
+ values[1] =-y;
+ values[2] = z;
+ break;
+ case SENSOR_ORIENTATION:
+ case SENSOR_ORIENTATION_RAW:
+ values[0] = (x >= 180) ? (x - 180) : (x + 180);
+ values[1] =-y;
+ values[2] =-z;
+ break;
+ }
+ }
+ }
+
+ private static int getLegacySensorType(int type) {
+ switch (type) {
+ case Sensor.TYPE_ACCELEROMETER:
+ return SENSOR_ACCELEROMETER;
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ return SENSOR_MAGNETIC_FIELD;
+ case Sensor.TYPE_ORIENTATION:
+ return SENSOR_ORIENTATION_RAW;
+ case Sensor.TYPE_TEMPERATURE:
+ return SENSOR_TEMPERATURE;
+ }
+ return 0;
+ }
+ }
+
+ private static final class LmsFilter {
+ private static final int SENSORS_RATE_MS = 20;
+ private static final int COUNT = 12;
+ private static final float PREDICTION_RATIO = 1.0f/3.0f;
+ private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
+ private float mV[] = new float[COUNT*2];
+ private float mT[] = new float[COUNT*2];
+ private int mIndex;
+
+ public LmsFilter() {
+ mIndex = COUNT;
+ }
+
+ public float filter(long time, float in) {
+ float v = in;
+ final float ns = 1.0f / 1000000000.0f;
+ final float t = time*ns;
+ float v1 = mV[mIndex];
+ if ((v-v1) > 180) {
+ v -= 360;
+ } else if ((v1-v) > 180) {
+ v += 360;
+ }
+ /* Manage the circular buffer, we write the data twice spaced
+ * by COUNT values, so that we don't have to copy the array
+ * when it's full
+ */
+ mIndex++;
+ if (mIndex >= COUNT*2)
+ mIndex = COUNT;
+ mV[mIndex] = v;
+ mT[mIndex] = t;
+ mV[mIndex-COUNT] = v;
+ mT[mIndex-COUNT] = t;
+
+ float A, B, C, D, E;
+ float a, b;
+ int i;
+
+ A = B = C = D = E = 0;
+ for (i=0 ; i<COUNT-1 ; i++) {
+ final int j = mIndex - 1 - i;
+ final float Z = mV[j];
+ final float T = 0.5f*(mT[j] + mT[j+1]) - t;
+ float dT = mT[j] - mT[j+1];
+ dT *= dT;
+ A += Z*dT;
+ B += T*(T*dT);
+ C += (T*dT);
+ D += Z*(T*dT);
+ E += dT;
+ }
+ b = (A*B + C*D) / (E*B + C*C);
+ a = (E*b - A) / C;
+ float f = b + PREDICTION_TIME*a;
+
+ // Normalize
+ f *= (1.0f / 360.0f);
+ if (((f>=0)?f:-f) >= 0.5f)
+ f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
+ if (f < 0)
+ f += 1.0f;
+ f *= 360.0f;
+ return f;
+ }
+ }
+
+ /**
+ * Sensor event pool implementation.
+ * @hide
+ */
+ protected static final class SensorEventPool {
+ private final int mPoolSize;
+ private final SensorEvent mPool[];
+ private int mNumItemsInPool;
+
+ private SensorEvent createSensorEvent() {
+ // maximal size for all legacy events is 3
+ return new SensorEvent(3);
+ }
+
+ SensorEventPool(int poolSize) {
+ mPoolSize = poolSize;
+ mNumItemsInPool = poolSize;
+ mPool = new SensorEvent[poolSize];
+ }
+
+ SensorEvent getFromPool() {
+ SensorEvent t = null;
+ synchronized (this) {
+ if (mNumItemsInPool > 0) {
+ // remove the "top" item from the pool
+ final int index = mPoolSize - mNumItemsInPool;
+ t = mPool[index];
+ mPool[index] = null;
+ mNumItemsInPool--;
+ }
+ }
+ if (t == null) {
+ // the pool was empty or this item was removed from the pool for
+ // the first time. In any case, we need to create a new item.
+ t = createSensorEvent();
+ }
+ return t;
+ }
+
+ void returnToPool(SensorEvent t) {
+ synchronized (this) {
+ // is there space left in the pool?
+ if (mNumItemsInPool < mPoolSize) {
+ // if so, return the item to the pool
+ mNumItemsInPool++;
+ final int index = mPoolSize - mNumItemsInPool;
+ mPool[index] = t;
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
new file mode 100644
index 0000000..7763405
--- /dev/null
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.os.Looper;
+import android.os.Process;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sensor manager implementation that communicates with the built-in
+ * system sensors.
+ *
+ * @hide
+ */
+public class SystemSensorManager extends SensorManager {
+ private static final String TAG = "SensorManager";
+
+ /*-----------------------------------------------------------------------*/
+
+ final Looper mMainLooper;
+
+ /*-----------------------------------------------------------------------*/
+
+ private static final int SENSOR_DISABLE = -1;
+ private static boolean sSensorModuleInitialized = false;
+ private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
+ /* The thread and the sensor list are global to the process
+ * but the actual thread is spawned on demand */
+ private static SensorThread sSensorThread;
+ private static int sQueue;
+
+ // Used within this module from outside SensorManager, don't make private
+ static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
+ static final ArrayList<ListenerDelegate> sListeners =
+ new ArrayList<ListenerDelegate>();
+
+ /*-----------------------------------------------------------------------*/
+
+ private static SensorEventPool sPool;
+
+ /*-----------------------------------------------------------------------*/
+
+ static private class SensorThread {
+
+ Thread mThread;
+ boolean mSensorsReady;
+
+ SensorThread() {
+ }
+
+ @Override
+ protected void finalize() {
+ }
+
+ // must be called with sListeners lock
+ boolean startLocked() {
+ try {
+ if (mThread == null) {
+ mSensorsReady = false;
+ SensorThreadRunnable runnable = new SensorThreadRunnable();
+ Thread thread = new Thread(runnable, SensorThread.class.getName());
+ thread.start();
+ synchronized (runnable) {
+ while (mSensorsReady == false) {
+ runnable.wait();
+ }
+ }
+ mThread = thread;
+ }
+ } catch (InterruptedException e) {
+ }
+ return mThread == null ? false : true;
+ }
+
+ private class SensorThreadRunnable implements Runnable {
+ SensorThreadRunnable() {
+ }
+
+ private boolean open() {
+ // NOTE: this cannot synchronize on sListeners, since
+ // it's held in the main thread at least until we
+ // return from here.
+ sQueue = sensors_create_queue();
+ return true;
+ }
+
+ public void run() {
+ //Log.d(TAG, "entering main sensor thread");
+ final float[] values = new float[3];
+ final int[] status = new int[1];
+ final long timestamp[] = new long[1];
+ Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
+
+ if (!open()) {
+ return;
+ }
+
+ synchronized (this) {
+ // we've open the driver, we're ready to open the sensors
+ mSensorsReady = true;
+ this.notify();
+ }
+
+ while (true) {
+ // wait for an event
+ final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
+
+ int accuracy = status[0];
+ synchronized (sListeners) {
+ if (sensor == -1 || sListeners.isEmpty()) {
+ // we lost the connection to the event stream. this happens
+ // when the last listener is removed or if there is an error
+ if (sensor == -1 && !sListeners.isEmpty()) {
+ // log a warning in case of abnormal termination
+ Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
+ }
+ // we have no more listeners or polling failed, terminate the thread
+ sensors_destroy_queue(sQueue);
+ sQueue = 0;
+ mThread = null;
+ break;
+ }
+ final Sensor sensorObject = sHandleToSensor.get(sensor);
+ if (sensorObject != null) {
+ // report the sensor event to all listeners that
+ // care about it.
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate listener = sListeners.get(i);
+ if (listener.hasSensor(sensorObject)) {
+ // this is asynchronous (okay to call
+ // with sListeners lock held).
+ listener.onSensorChangedLocked(sensorObject,
+ values, timestamp, accuracy);
+ }
+ }
+ }
+ }
+ }
+ //Log.d(TAG, "exiting main sensor thread");
+ }
+ }
+ }
+
+ /*-----------------------------------------------------------------------*/
+
+ private class ListenerDelegate {
+ private final SensorEventListener mSensorEventListener;
+ private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
+ private final Handler mHandler;
+ public SparseBooleanArray mSensors = new SparseBooleanArray();
+ public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+ public SparseIntArray mSensorAccuracies = new SparseIntArray();
+
+ ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
+ mSensorEventListener = listener;
+ Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
+ // currently we create one Handler instance per listener, but we could
+ // have one per looper (we'd need to pass the ListenerDelegate
+ // instance to handleMessage and keep track of them separately).
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ final SensorEvent t = (SensorEvent)msg.obj;
+ final int handle = t.sensor.getHandle();
+
+ switch (t.sensor.getType()) {
+ // Only report accuracy for sensors that support it.
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ case Sensor.TYPE_ORIENTATION:
+ // call onAccuracyChanged() only if the value changes
+ final int accuracy = mSensorAccuracies.get(handle);
+ if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+ mSensorAccuracies.put(handle, t.accuracy);
+ mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
+ }
+ break;
+ default:
+ // For other sensors, just report the accuracy once
+ if (mFirstEvent.get(handle) == false) {
+ mFirstEvent.put(handle, true);
+ mSensorEventListener.onAccuracyChanged(
+ t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
+ }
+ break;
+ }
+
+ mSensorEventListener.onSensorChanged(t);
+ sPool.returnToPool(t);
+ }
+ };
+ addSensor(sensor);
+ }
+
+ Object getListener() {
+ return mSensorEventListener;
+ }
+
+ void addSensor(Sensor sensor) {
+ mSensors.put(sensor.getHandle(), true);
+ mSensorList.add(sensor);
+ }
+ int removeSensor(Sensor sensor) {
+ mSensors.delete(sensor.getHandle());
+ mSensorList.remove(sensor);
+ return mSensors.size();
+ }
+ boolean hasSensor(Sensor sensor) {
+ return mSensors.get(sensor.getHandle());
+ }
+ List<Sensor> getSensors() {
+ return mSensorList;
+ }
+
+ void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
+ SensorEvent t = sPool.getFromPool();
+ final float[] v = t.values;
+ v[0] = values[0];
+ v[1] = values[1];
+ v[2] = values[2];
+ t.timestamp = timestamp[0];
+ t.accuracy = accuracy;
+ t.sensor = sensor;
+ Message msg = Message.obtain();
+ msg.what = 0;
+ msg.obj = t;
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public SystemSensorManager(Looper mainLooper) {
+ mMainLooper = mainLooper;
+
+ synchronized(sListeners) {
+ if (!sSensorModuleInitialized) {
+ sSensorModuleInitialized = true;
+
+ nativeClassInit();
+
+ // initialize the sensor list
+ sensors_module_init();
+ final ArrayList<Sensor> fullList = sFullSensorsList;
+ int i = 0;
+ do {
+ Sensor sensor = new Sensor();
+ i = sensors_module_get_next_sensor(sensor, i);
+
+ if (i>=0) {
+ //Log.d(TAG, "found sensor: " + sensor.getName() +
+ // ", handle=" + sensor.getHandle());
+ fullList.add(sensor);
+ sHandleToSensor.append(sensor.getHandle(), sensor);
+ }
+ } while (i>0);
+
+ sPool = new SensorEventPool( sFullSensorsList.size()*2 );
+ sSensorThread = new SensorThread();
+ }
+ }
+ }
+
+ /** @hide */
+ @Override
+ protected List<Sensor> getFullSensorList() {
+ return sFullSensorsList;
+ }
+
+ private boolean enableSensorLocked(Sensor sensor, int delay) {
+ boolean result = false;
+ for (ListenerDelegate i : sListeners) {
+ if (i.hasSensor(sensor)) {
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ result = sensors_enable_sensor(sQueue, name, handle, delay);
+ break;
+ }
+ }
+ return result;
+ }
+
+ private boolean disableSensorLocked(Sensor sensor) {
+ for (ListenerDelegate i : sListeners) {
+ if (i.hasSensor(sensor)) {
+ // not an error, it's just that this sensor is still in use
+ return true;
+ }
+ }
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
+ }
+
+ /** @hide */
+ protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
+ int delay, Handler handler) {
+ boolean result = true;
+ synchronized (sListeners) {
+ // look for this listener in our list
+ ListenerDelegate l = null;
+ for (ListenerDelegate i : sListeners) {
+ if (i.getListener() == listener) {
+ l = i;
+ break;
+ }
+ }
+
+ // if we don't find it, add it to the list
+ if (l == null) {
+ l = new ListenerDelegate(listener, sensor, handler);
+ sListeners.add(l);
+ // if the list is not empty, start our main thread
+ if (!sListeners.isEmpty()) {
+ if (sSensorThread.startLocked()) {
+ if (!enableSensorLocked(sensor, delay)) {
+ // oops. there was an error
+ sListeners.remove(l);
+ result = false;
+ }
+ } else {
+ // there was an error, remove the listener
+ sListeners.remove(l);
+ result = false;
+ }
+ } else {
+ // weird, we couldn't add the listener
+ result = false;
+ }
+ } else if (!l.hasSensor(sensor)) {
+ l.addSensor(sensor);
+ if (!enableSensorLocked(sensor, delay)) {
+ // oops. there was an error
+ l.removeSensor(sensor);
+ result = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /** @hide */
+ @Override
+ protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+ synchronized (sListeners) {
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate l = sListeners.get(i);
+ if (l.getListener() == listener) {
+ if (sensor == null) {
+ sListeners.remove(i);
+ // disable all sensors for this listener
+ for (Sensor s : l.getSensors()) {
+ disableSensorLocked(s);
+ }
+ } else if (l.removeSensor(sensor) == 0) {
+ // if we have no more sensors enabled on this listener,
+ // take it off the list.
+ sListeners.remove(i);
+ disableSensorLocked(sensor);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private static native void nativeClassInit();
+
+ private static native int sensors_module_init();
+ private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+
+ // Used within this module from outside SensorManager, don't make private
+ static native int sensors_create_queue();
+ static native void sensors_destroy_queue(int queue);
+ static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
+ static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
+}
diff --git a/core/java/android/hardware/input/IInputDevicesChangedListener.aidl b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl
new file mode 100644
index 0000000..5d8ada1
--- /dev/null
+++ b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+/** @hide */
+interface IInputDevicesChangedListener {
+ /* Called when input devices changed, such as a device being added,
+ * removed or changing configuration.
+ *
+ * The parameter is an array of pairs (deviceId, generation) indicating the current
+ * device id and generation of all input devices. The client can determine what
+ * has happened by comparing the result to its prior observations.
+ */
+ oneway void onInputDevicesChanged(in int[] deviceIdAndGeneration);
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 47e0d1e..3137947 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -17,6 +17,8 @@
package android.hardware.input;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.IInputDevicesChangedListener;
+import android.os.IBinder;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -42,4 +44,11 @@
String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
String keyboardLayoutDescriptor);
+
+ // Registers an input devices changed listener.
+ void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
+
+ // Input device vibrator control.
+ void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
+ void cancelVibrate(int deviceId, IBinder token);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 3b3c237..b39b823 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,9 +19,14 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -29,6 +34,8 @@
import android.view.InputDevice;
import android.view.InputEvent;
+import java.util.ArrayList;
+
/**
* Provides information about input devices and available key layouts.
* <p>
@@ -40,11 +47,22 @@
*/
public final class InputManager {
private static final String TAG = "InputManager";
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_DEVICE_ADDED = 1;
+ private static final int MSG_DEVICE_REMOVED = 2;
+ private static final int MSG_DEVICE_CHANGED = 3;
private static InputManager sInstance;
private final IInputManager mIm;
- private final SparseArray<InputDevice> mInputDevices = new SparseArray<InputDevice>();
+
+ // Guarded by mInputDevicesLock
+ private final Object mInputDevicesLock = new Object();
+ private SparseArray<InputDevice> mInputDevices;
+ private InputDevicesChangedListener mInputDevicesChangedListener;
+ private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
+ new ArrayList<InputDeviceListenerDelegate>();
/**
* Broadcast Action: Query available keyboard layouts.
@@ -169,6 +187,103 @@
}
/**
+ * Gets information about the input device with the specified id.
+ * @param id The device id.
+ * @return The input device or null if not found.
+ */
+ public InputDevice getInputDevice(int id) {
+ synchronized (mInputDevicesLock) {
+ populateInputDevicesLocked();
+
+ int index = mInputDevices.indexOfKey(id);
+ if (index < 0) {
+ return null;
+ }
+
+ InputDevice inputDevice = mInputDevices.valueAt(index);
+ if (inputDevice == null) {
+ try {
+ inputDevice = mIm.getInputDevice(id);
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Could not get input device information.", ex);
+ }
+ }
+ mInputDevices.setValueAt(index, inputDevice);
+ return inputDevice;
+ }
+ }
+
+ /**
+ * Gets the ids of all input devices in the system.
+ * @return The input device ids.
+ */
+ public int[] getInputDeviceIds() {
+ synchronized (mInputDevicesLock) {
+ populateInputDevicesLocked();
+
+ final int count = mInputDevices.size();
+ final int[] ids = new int[count];
+ for (int i = 0; i < count; i++) {
+ ids[i] = mInputDevices.keyAt(i);
+ }
+ return ids;
+ }
+ }
+
+ /**
+ * Registers an input device listener to receive notifications about when
+ * input devices are added, removed or changed.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ *
+ * @see #unregisterInputDeviceListener
+ */
+ public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mInputDevicesLock) {
+ int index = findInputDeviceListenerLocked(listener);
+ if (index < 0) {
+ mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Unregisters an input device listener.
+ *
+ * @param listener The listener to unregister.
+ *
+ * @see #registerInputDeviceListener
+ */
+ public void unregisterInputDeviceListener(InputDeviceListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mInputDevicesLock) {
+ int index = findInputDeviceListenerLocked(listener);
+ if (index >= 0) {
+ mInputDeviceListeners.remove(index);
+ }
+ }
+ }
+
+ private int findInputDeviceListenerLocked(InputDeviceListener listener) {
+ final int numListeners = mInputDeviceListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (mInputDeviceListeners.get(i).mListener == listener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
* Gets information about all supported keyboard layouts.
* <p>
* The input manager consults the built-in keyboard layouts as well
@@ -327,50 +442,6 @@
}
/**
- * Gets information about the input device with the specified id.
- * @param id The device id.
- * @return The input device or null if not found.
- *
- * @hide
- */
- public InputDevice getInputDevice(int id) {
- synchronized (mInputDevices) {
- InputDevice inputDevice = mInputDevices.get(id);
- if (inputDevice != null) {
- return inputDevice;
- }
- }
- final InputDevice newInputDevice;
- try {
- newInputDevice = mIm.getInputDevice(id);
- } catch (RemoteException ex) {
- throw new RuntimeException("Could not get input device information.", ex);
- }
- synchronized (mInputDevices) {
- InputDevice inputDevice = mInputDevices.get(id);
- if (inputDevice != null) {
- return inputDevice;
- }
- mInputDevices.put(id, newInputDevice);
- return newInputDevice;
- }
- }
-
- /**
- * Gets the ids of all input devices in the system.
- * @return The input device ids.
- *
- * @hide
- */
- public int[] getInputDeviceIds() {
- try {
- return mIm.getInputDeviceIds();
- } catch (RemoteException ex) {
- throw new RuntimeException("Could not get input device ids.", ex);
- }
- }
-
- /**
* Queries the framework about whether any physical keys exist on the
* any keyboard attached to the device that are capable of producing the given
* array of key codes.
@@ -429,4 +500,201 @@
return false;
}
}
+
+ private void populateInputDevicesLocked() {
+ if (mInputDevicesChangedListener == null) {
+ final InputDevicesChangedListener listener = new InputDevicesChangedListener();
+ try {
+ mIm.registerInputDevicesChangedListener(listener);
+ } catch (RemoteException ex) {
+ throw new RuntimeException(
+ "Could not get register input device changed listener", ex);
+ }
+ mInputDevicesChangedListener = listener;
+ }
+
+ if (mInputDevices == null) {
+ final int[] ids;
+ try {
+ ids = mIm.getInputDeviceIds();
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Could not get input device ids.", ex);
+ }
+
+ mInputDevices = new SparseArray<InputDevice>();
+ for (int i = 0; i < ids.length; i++) {
+ mInputDevices.put(ids[i], null);
+ }
+ }
+ }
+
+ private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
+ if (DEBUG) {
+ Log.d(TAG, "Received input devices changed.");
+ }
+
+ synchronized (mInputDevicesLock) {
+ for (int i = mInputDevices.size(); --i > 0; ) {
+ final int deviceId = mInputDevices.keyAt(i);
+ if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
+ if (DEBUG) {
+ Log.d(TAG, "Device removed: " + deviceId);
+ }
+ mInputDevices.removeAt(i);
+ sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
+ }
+ }
+
+ for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
+ final int deviceId = deviceIdAndGeneration[i];
+ int index = mInputDevices.indexOfKey(deviceId);
+ if (index >= 0) {
+ final InputDevice device = mInputDevices.valueAt(index);
+ if (device != null) {
+ final int generation = deviceIdAndGeneration[i + 1];
+ if (device.getGeneration() != generation) {
+ if (DEBUG) {
+ Log.d(TAG, "Device changed: " + deviceId);
+ }
+ mInputDevices.setValueAt(index, null);
+ sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
+ }
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Device added: " + deviceId);
+ }
+ mInputDevices.put(deviceId, null);
+ sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
+ }
+ }
+ }
+ }
+
+ private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
+ final int numListeners = mInputDeviceListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
+ listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
+ }
+ }
+
+ private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
+ for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
+ if (deviceIdAndGeneration[i] == deviceId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a vibrator service associated with an input device, assuming it has one.
+ * @return The vibrator, never null.
+ * @hide
+ */
+ public Vibrator getInputDeviceVibrator(int deviceId) {
+ return new InputDeviceVibrator(deviceId);
+ }
+
+ /**
+ * Listens for changes in input devices.
+ */
+ public interface InputDeviceListener {
+ /**
+ * Called whenever an input device has been added to the system.
+ * Use {@link InputManager#getInputDevice} to get more information about the device.
+ *
+ * @param deviceId The id of the input device that was added.
+ */
+ void onInputDeviceAdded(int deviceId);
+
+ /**
+ * Called whenever an input device has been removed from the system.
+ *
+ * @param deviceId The id of the input device that was removed.
+ */
+ void onInputDeviceRemoved(int deviceId);
+
+ /**
+ * Called whenever the properties of an input device have changed since they
+ * were last queried. Use {@link InputManager#getInputDevice} to get
+ * a fresh {@link InputDevice} object with the new properties.
+ *
+ * @param deviceId The id of the input device that changed.
+ */
+ void onInputDeviceChanged(int deviceId);
+ }
+
+ private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
+ @Override
+ public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
+ InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
+ }
+ }
+
+ private static final class InputDeviceListenerDelegate extends Handler {
+ public final InputDeviceListener mListener;
+
+ public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
+ super(handler != null ? handler.getLooper() : Looper.myLooper());
+ mListener = listener;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DEVICE_ADDED:
+ mListener.onInputDeviceAdded(msg.arg1);
+ break;
+ case MSG_DEVICE_REMOVED:
+ mListener.onInputDeviceRemoved(msg.arg1);
+ break;
+ case MSG_DEVICE_CHANGED:
+ mListener.onInputDeviceChanged(msg.arg1);
+ break;
+ }
+ }
+ }
+
+ private final class InputDeviceVibrator extends Vibrator {
+ private final int mDeviceId;
+ private final Binder mToken;
+
+ public InputDeviceVibrator(int deviceId) {
+ mDeviceId = deviceId;
+ mToken = new Binder();
+ }
+
+ @Override
+ public boolean hasVibrator() {
+ return true;
+ }
+
+ @Override
+ public void vibrate(long milliseconds) {
+ vibrate(new long[] { 0, milliseconds}, -1);
+ }
+
+ @Override
+ public void vibrate(long[] pattern, int repeat) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ try {
+ mIm.vibrate(mDeviceId, pattern, repeat, mToken);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to vibrate.", ex);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ try {
+ mIm.cancelVibrate(mDeviceId, mToken);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to cancel vibration.", ex);
+ }
+ }
+ }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index de16985..ef4209f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -373,10 +373,11 @@
}
/**
- * Gets you info about the current data network.
- * Call {@link NetworkInfo#isConnected()} on the returned {@link NetworkInfo}
- * to check if the device has a data connection.
- */
+ * Returns details about the currently active data network. When connected,
+ * this network is the default route for outgoing connections. You should
+ * always check {@link NetworkInfo#isConnected()} before initiating network
+ * traffic. This may return {@code null} when no networks are available.
+ */
public NetworkInfo getActiveNetworkInfo() {
try {
return mService.getActiveNetworkInfo();
@@ -856,4 +857,19 @@
} catch (RemoteException e) {}
return false;
}
+
+ /**
+ * Returns if the currently active data network is metered. A network is
+ * classified as metered when the user is sensitive to heavy data usage on
+ * that connection. You should check this before doing large data transfers,
+ * and warn the user or delay the operation until another network is
+ * available.
+ */
+ public boolean isActiveNetworkMetered() {
+ try {
+ return mService.isActiveNetworkMetered();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 7046008..92aeff2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -51,6 +51,7 @@
NetworkState[] getAllNetworkState();
NetworkQuotaInfo getActiveNetworkQuotaInfo();
+ boolean isActiveNetworkMetered();
boolean setRadios(boolean onOff);
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 89c9c36..3250ae7 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -32,6 +32,7 @@
/** Control UID policies. */
void setAppPolicy(int appId, int policy);
int getAppPolicy(int appId);
+ int[] getAppsWithPolicy(int policy);
boolean isUidForeground(int uid);
@@ -50,5 +51,6 @@
boolean getRestrictBackground();
NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
+ boolean isNetworkMetered(in NetworkState state);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index b4f6367..08d4c6c 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -32,6 +32,9 @@
/** Return data layer snapshot of UID network usage. */
NetworkStats getDataLayerSnapshotForUid(int uid);
+ /** Return set of any ifaces associated with mobile networks since boot. */
+ String[] getMobileIfaces();
+
/** Increment data layer count of operations performed for UID and tag. */
void incrementOperationCount(int uid, int tag, int operationCount);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2b36131..07bfd4b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,6 +92,14 @@
}
}
+ public int[] getAppsWithPolicy(int policy) {
+ try {
+ return mService.getAppsWithPolicy(policy);
+ } catch (RemoteException e) {
+ return new int[0];
+ }
+ }
+
public void registerListener(INetworkPolicyListener listener) {
try {
mService.registerListener(listener);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 50432a1..39a4d7b 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,8 @@
public static final int MATCH_MOBILE_4G = 3;
public static final int MATCH_WIFI = 4;
public static final int MATCH_ETHERNET = 5;
+ public static final int MATCH_MOBILE_WILDCARD = 6;
+ public static final int MATCH_WIFI_WILDCARD = 7;
/**
* Set of {@link NetworkInfo#getType()} that reflect data usage.
@@ -86,11 +88,19 @@
}
/**
+ * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks,
+ * regardless of IMSI.
+ */
+ public static NetworkTemplate buildTemplateMobileWildcard() {
+ return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);
+ }
+
+ /**
* Template to match all {@link ConnectivityManager#TYPE_WIFI} networks,
* regardless of SSID.
*/
public static NetworkTemplate buildTemplateWifiWildcard() {
- return new NetworkTemplate(MATCH_WIFI, null, null);
+ return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
}
@Deprecated
@@ -198,6 +208,10 @@
return matchesWifi(ident);
case MATCH_ETHERNET:
return matchesEthernet(ident);
+ case MATCH_MOBILE_WILDCARD:
+ return matchesMobileWildcard(ident);
+ case MATCH_WIFI_WILDCARD:
+ return matchesWifiWildcard(ident);
default:
throw new IllegalArgumentException("unknown network template");
}
@@ -257,13 +271,7 @@
private boolean matchesWifi(NetworkIdentity ident) {
switch (ident.mType) {
case TYPE_WIFI:
- if (mNetworkId == null) {
- return true;
- } else {
- return Objects.equal(mNetworkId, ident.mNetworkId);
- }
- case TYPE_WIFI_P2P:
- return mNetworkId == null;
+ return Objects.equal(mNetworkId, ident.mNetworkId);
default:
return false;
}
@@ -279,6 +287,24 @@
return false;
}
+ private boolean matchesMobileWildcard(NetworkIdentity ident) {
+ if (ident.mType == TYPE_WIMAX) {
+ return true;
+ } else {
+ return contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
+ }
+ }
+
+ private boolean matchesWifiWildcard(NetworkIdentity ident) {
+ switch (ident.mType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private static String getMatchRuleName(int matchRule) {
switch (matchRule) {
case MATCH_MOBILE_3G_LOWER:
@@ -291,6 +317,10 @@
return "WIFI";
case MATCH_ETHERNET:
return "ETHERNET";
+ case MATCH_MOBILE_WILDCARD:
+ return "MOBILE_WILDCARD";
+ case MATCH_WIFI_WILDCARD:
+ return "WIFI_WILDCARD";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index ee3e165..e437d2e 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -88,6 +88,16 @@
*/
public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;
+ private static INetworkStatsService sStatsService;
+
+ private synchronized static INetworkStatsService getStatsService() {
+ if (sStatsService == null) {
+ sStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ }
+ return sStatsService;
+ }
+
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
@@ -228,11 +238,9 @@
* @param operationCount Number of operations to increment count by.
*/
public static void incrementOperationCount(int tag, int operationCount) {
- final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
final int uid = android.os.Process.myUid();
try {
- statsService.incrementOperationCount(uid, tag, operationCount);
+ getStatsService().incrementOperationCount(uid, tag, operationCount);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -257,7 +265,13 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getMobileTxPackets();
+ public static long getMobileTxPackets() {
+ long total = 0;
+ for (String iface : getMobileIfaces()) {
+ total += getTxPackets(iface);
+ }
+ return total;
+ }
/**
* Get the total number of packets received through the mobile interface.
@@ -265,7 +279,13 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getMobileRxPackets();
+ public static long getMobileRxPackets() {
+ long total = 0;
+ for (String iface : getMobileIfaces()) {
+ total += getRxPackets(iface);
+ }
+ return total;
+ }
/**
* Get the total number of bytes transmitted through the mobile interface.
@@ -273,7 +293,13 @@
* @return number of bytes. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getMobileTxBytes();
+ public static long getMobileTxBytes() {
+ long total = 0;
+ for (String iface : getMobileIfaces()) {
+ total += getTxBytes(iface);
+ }
+ return total;
+ }
/**
* Get the total number of bytes received through the mobile interface.
@@ -281,7 +307,13 @@
* @return number of bytes. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getMobileRxBytes();
+ public static long getMobileRxBytes() {
+ long total = 0;
+ for (String iface : getMobileIfaces()) {
+ total += getRxBytes(iface);
+ }
+ return total;
+ }
/**
* Get the total number of packets transmitted through the specified interface.
@@ -290,7 +322,9 @@
* {@link #UNSUPPORTED} will be returned.
* @hide
*/
- public static native long getTxPackets(String iface);
+ public static long getTxPackets(String iface) {
+ return nativeGetIfaceStat(iface, TYPE_TX_PACKETS);
+ }
/**
* Get the total number of packets received through the specified interface.
@@ -299,7 +333,9 @@
* {@link #UNSUPPORTED} will be returned.
* @hide
*/
- public static native long getRxPackets(String iface);
+ public static long getRxPackets(String iface) {
+ return nativeGetIfaceStat(iface, TYPE_RX_PACKETS);
+ }
/**
* Get the total number of bytes transmitted through the specified interface.
@@ -308,7 +344,9 @@
* {@link #UNSUPPORTED} will be returned.
* @hide
*/
- public static native long getTxBytes(String iface);
+ public static long getTxBytes(String iface) {
+ return nativeGetIfaceStat(iface, TYPE_TX_BYTES);
+ }
/**
* Get the total number of bytes received through the specified interface.
@@ -317,8 +355,9 @@
* {@link #UNSUPPORTED} will be returned.
* @hide
*/
- public static native long getRxBytes(String iface);
-
+ public static long getRxBytes(String iface) {
+ return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
+ }
/**
* Get the total number of packets sent through all network interfaces.
@@ -326,7 +365,9 @@
* @return the number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getTotalTxPackets();
+ public static long getTotalTxPackets() {
+ return nativeGetTotalStat(TYPE_TX_PACKETS);
+ }
/**
* Get the total number of packets received through all network interfaces.
@@ -334,7 +375,9 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getTotalRxPackets();
+ public static long getTotalRxPackets() {
+ return nativeGetTotalStat(TYPE_RX_PACKETS);
+ }
/**
* Get the total number of bytes sent through all network interfaces.
@@ -342,7 +385,9 @@
* @return number of bytes. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getTotalTxBytes();
+ public static long getTotalTxBytes() {
+ return nativeGetTotalStat(TYPE_TX_BYTES);
+ }
/**
* Get the total number of bytes received through all network interfaces.
@@ -350,7 +395,9 @@
* @return number of bytes. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static native long getTotalRxBytes();
+ public static long getTotalRxBytes() {
+ return nativeGetTotalStat(TYPE_RX_BYTES);
+ }
/**
* Get the number of bytes sent through the network for this UID.
@@ -483,7 +530,6 @@
*/
public static native long getUidTcpRxSegments(int uid);
-
/**
* Get the number of UDP packets sent for this UID.
* Includes DNS requests.
@@ -515,13 +561,33 @@
* special permission.
*/
private static NetworkStats getDataLayerSnapshotForUid(Context context) {
- final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
final int uid = android.os.Process.myUid();
try {
- return statsService.getDataLayerSnapshotForUid(uid);
+ return getStatsService().getDataLayerSnapshotForUid(uid);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
+
+ /**
+ * Return set of any ifaces associated with mobile networks since boot.
+ * Interfaces are never removed from this list, so counters should always be
+ * monotonic.
+ */
+ private static String[] getMobileIfaces() {
+ try {
+ return getStatsService().getMobileIfaces();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // NOTE: keep these in sync with android_net_TrafficStats.cpp
+ private static final int TYPE_RX_BYTES = 0;
+ private static final int TYPE_RX_PACKETS = 1;
+ private static final int TYPE_TX_BYTES = 2;
+ private static final int TYPE_TX_PACKETS = 3;
+
+ private static native long nativeGetTotalStat(int type);
+ private static native long nativeGetIfaceStat(String iface, int type);
}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index 4e79822..1c6d5d0 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -25,7 +25,6 @@
interface INdefPushCallback
{
NdefMessage createMessage();
- Uri getUri();
- String getMimeType();
+ Uri[] getUris();
void onNdefPushComplete();
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index f80dae4..7ffa575 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -108,8 +108,8 @@
NdefMessage ndefMessage = null; // static NDEF message
NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
- Uri uri = null;
- String mimeType = null;
+ NfcAdapter.CreateBeamUrisCallback uriCallback = null;
+ Uri[] uris = null;
public NfcActivityState(Activity activity) {
if (activity.getWindow().isDestroyed()) {
throw new IllegalStateException("activity is already destroyed");
@@ -128,14 +128,19 @@
ndefMessage = null;
ndefMessageCallback = null;
onNdefPushCompleteCallback = null;
- uri = null;
- mimeType = null;
+ uriCallback = null;
+ uris = null;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("[").append(" ");
s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
- s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
+ s.append(uriCallback).append(" ");
+ if (uris != null) {
+ for (Uri uri : uris) {
+ s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
+ }
+ }
return s.toString();
}
}
@@ -184,12 +189,25 @@
mDefaultEvent = new NfcEvent(mAdapter);
}
- public void setNdefPushContentUri(Activity activity, String mimeType, Uri uri) {
+ public void setNdefPushContentUri(Activity activity, Uri[] uris) {
boolean isResumed;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
- state.uri = uri;
- state.mimeType = mimeType;
+ state.uris = uris;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ requestNfcServiceCallback(true);
+ }
+ }
+
+
+ public void setNdefPushContentUriCallback(Activity activity,
+ NfcAdapter.CreateBeamUrisCallback callback) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.uriCallback = callback;
isResumed = state.resumed;
}
if (isResumed) {
@@ -271,24 +289,22 @@
/** Callback from NFC service, usually on binder thread */
@Override
- public Uri getUri() {
+ public Uri[] getUris() {
+ Uri[] uris;
+ NfcAdapter.CreateBeamUrisCallback callback;
synchronized (NfcActivityManager.this) {
NfcActivityState state = findResumedActivityState();
if (state == null) return null;
-
- return state.uri;
+ uris = state.uris;
+ callback = state.uriCallback;
+ }
+ if (callback != null) {
+ return callback.createBeamUris(mDefaultEvent);
+ } else {
+ return uris;
}
}
- /** Callback from NFC service, usually on binder thread */
- @Override
- public String getMimeType() {
- synchronized (NfcActivityManager.this) {
- NfcActivityState state = findResumedActivityState();
- if (state == null) return null;
- return state.mimeType;
- }
- }
/** Callback from NFC service, usually on binder thread */
@Override
public void onNdefPushComplete() {
@@ -358,4 +374,5 @@
}
}
}
+
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 917751c..90f5bef 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -203,6 +203,27 @@
/** @hide */
public static final int STATE_TURNING_OFF = 4;
+ /** @hide */
+ public static final String ACTION_HANDOVER_TRANSFER_STARTED =
+ "android.nfc.action.HANDOVER_TRANSFER_STARTED";
+
+ /** @hide */
+ public static final String ACTION_HANDOVER_TRANSFER_DONE =
+ "android.nfc.action.HANDOVER_TRANSFER_DONE";
+
+ /** @hide */
+ public static final String EXTRA_HANDOVER_TRANSFER_STATUS =
+ "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
+
+ /** @hide */
+ public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
+ /** @hide */
+ public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
+
+ /** @hide */
+ public static final String EXTRA_HANDOVER_TRANSFER_URI =
+ "android.nfc.extra.HANDOVER_TRANSFER_URI";
+
// Guarded by NfcAdapter.class
static boolean sIsInitialized = false;
@@ -281,6 +302,12 @@
public NdefMessage createNdefMessage(NfcEvent event);
}
+
+ // TODO javadoc
+ public interface CreateBeamUrisCallback {
+ public Uri[] createBeamUris(NfcEvent event);
+ }
+
/**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
@@ -556,16 +583,22 @@
}
}
- //TODO: Consider a callback alternative
- //TOOD: See if we get rid of mimeType
//TODO: make sure NFC service has permission for URI
+ //TODO: see if we will eventually support multiple URIs
//TODO: javadoc
- /** @hide */
- public void setBeamPushUri(String mimeType, Uri uri, Activity activity) {
+ public void setBeamPushUris(Uri[] uris, Activity activity) {
if (activity == null) {
throw new NullPointerException("activity cannot be null");
}
- mNfcActivityManager.setNdefPushContentUri(activity, mimeType, uri);
+ mNfcActivityManager.setNdefPushContentUri(activity, uris);
+ }
+
+ // TODO javadoc
+ public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushContentUriCallback(activity, callback);
}
/**
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
new file mode 100644
index 0000000..8de4e06
--- /dev/null
+++ b/core/java/android/os/NullVibrator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+/**
+ * Vibrator implementation that does nothing.
+ *
+ * @hide
+ */
+public class NullVibrator extends Vibrator {
+ private static final NullVibrator sInstance = new NullVibrator();
+
+ private NullVibrator() {
+ }
+
+ public static NullVibrator getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public boolean hasVibrator() {
+ return false;
+ }
+
+ @Override
+ public void vibrate(long milliseconds) {
+ }
+
+ @Override
+ public void vibrate(long[] pattern, int repeat) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ }
+}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
new file mode 100644
index 0000000..7c5a47e
--- /dev/null
+++ b/core/java/android/os/SystemVibrator.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+/**
+ * Vibrator implementation that controls the main system vibrator.
+ *
+ * @hide
+ */
+public class SystemVibrator extends Vibrator {
+ private static final String TAG = "Vibrator";
+
+ private final IVibratorService mService;
+ private final Binder mToken = new Binder();
+
+ public SystemVibrator() {
+ mService = IVibratorService.Stub.asInterface(
+ ServiceManager.getService("vibrator"));
+ }
+
+ @Override
+ public boolean hasVibrator() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ return false;
+ }
+ try {
+ return mService.hasVibrator();
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ @Override
+ public void vibrate(long milliseconds) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ return;
+ }
+ try {
+ mService.vibrate(milliseconds, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ }
+
+ @Override
+ public void vibrate(long[] pattern, int repeat) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ return;
+ }
+ // catch this here because the server will do nothing. pattern may
+ // not be null, let that be checked, because the server will drop it
+ // anyway
+ if (repeat < pattern.length) {
+ try {
+ mService.vibratePattern(pattern, repeat, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ } else {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.cancelVibrate(mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vibration.", e);
+ }
+ }
+}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 3769cfe..3f783c9 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,61 +16,37 @@
package android.os;
-import android.util.Log;
+import android.content.Context;
/**
* Class that operates the vibrator on the device.
* <p>
* If your process exits, any vibration you started with will stop.
* </p>
+ *
+ * To obtain an instance of the system vibrator, call
+ * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
*/
-public class Vibrator
-{
- private static final String TAG = "Vibrator";
-
- IVibratorService mService;
- private final Binder mToken = new Binder();
-
- /** @hide */
- public Vibrator()
- {
- mService = IVibratorService.Stub.asInterface(
- ServiceManager.getService("vibrator"));
+public abstract class Vibrator {
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ public Vibrator() {
}
/**
- * Check whether the hardware has a vibrator. Returns true if a vibrator
- * exists, else false.
+ * Check whether the hardware has a vibrator.
+ *
+ * @return True if the hardware has a vibrator, else false.
*/
- public boolean hasVibrator() {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
- return false;
- }
- try {
- return mService.hasVibrator();
- } catch (RemoteException e) {
- }
- return false;
- }
+ public abstract boolean hasVibrator();
/**
- * Turn the vibrator on.
+ * Vibrate constantly for the specified period of time.
*
* @param milliseconds The number of milliseconds to vibrate.
*/
- public void vibrate(long milliseconds)
- {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
- return;
- }
- try {
- mService.vibrate(milliseconds, mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to vibrate.", e);
- }
- }
+ public abstract void vibrate(long milliseconds);
/**
* Vibrate with a given pattern.
@@ -90,38 +66,10 @@
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
*/
- public void vibrate(long[] pattern, int repeat)
- {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
- return;
- }
- // catch this here because the server will do nothing. pattern may
- // not be null, let that be checked, because the server will drop it
- // anyway
- if (repeat < pattern.length) {
- try {
- mService.vibratePattern(pattern, repeat, mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to vibrate.", e);
- }
- } else {
- throw new ArrayIndexOutOfBoundsException();
- }
- }
+ public abstract void vibrate(long[] pattern, int repeat);
/**
* Turn the vibrator off.
*/
- public void cancel()
- {
- if (mService == null) {
- return;
- }
- try {
- mService.cancelVibrate(mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to cancel vibration.", e);
- }
- }
+ public abstract void cancel();
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e4729c7..035d8c4 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1536,7 +1536,11 @@
*
* @param resolver the ContentResolver to use
* @param contactId the person who was contacted
+ *
+ * @deprecated The class DataUsageStatUpdater of the Android support library should
+ * be used instead.
*/
+ @Deprecated
public static void markAsContacted(ContentResolver resolver, long contactId) {
Uri uri = ContentUris.withAppendedId(CONTENT_URI, contactId);
ContentValues values = new ContentValues();
@@ -7500,7 +7504,7 @@
* <p>
* Applications can also clear all usage information with:
* <pre>
- * boolean successful = resolver.delete(DataUsageFeedback.FEEDBACK_URI, null, null) > 0;
+ * boolean successful = resolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0;
* </pre>
* </p>
*/
@@ -7514,6 +7518,14 @@
Uri.withAppendedPath(Data.CONTENT_URI, "usagefeedback");
/**
+ * The content:// style URI for deleting all usage information.
+ * Must be used with {@link ContentResolver#delete(Uri, String, String[])}.
+ * The {@code where} and {@code selectionArgs} parameters are ignored.
+ */
+ public static final Uri DELETE_USAGE_URI =
+ Uri.withAppendedPath(Contacts.CONTENT_URI, "delete_usage");
+
+ /**
* <p>
* Name for query parameter specifying the type of data usage.
* </p>
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index bd6170b..cd8d51f 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -704,6 +704,37 @@
*/
public static final int STATUS_BLOCKED = 498;
+ /** {@hide} */
+ public static String statusToString(int status) {
+ switch (status) {
+ case STATUS_PENDING: return "PENDING";
+ case STATUS_RUNNING: return "RUNNING";
+ case STATUS_PAUSED_BY_APP: return "PAUSED_BY_APP";
+ case STATUS_WAITING_TO_RETRY: return "WAITING_TO_RETRY";
+ case STATUS_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
+ case STATUS_QUEUED_FOR_WIFI: return "QUEUED_FOR_WIFI";
+ case STATUS_INSUFFICIENT_SPACE_ERROR: return "INSUFFICIENT_SPACE_ERROR";
+ case STATUS_DEVICE_NOT_FOUND_ERROR: return "DEVICE_NOT_FOUND_ERROR";
+ case STATUS_SUCCESS: return "SUCCESS";
+ case STATUS_BAD_REQUEST: return "BAD_REQUEST";
+ case STATUS_NOT_ACCEPTABLE: return "NOT_ACCEPTABLE";
+ case STATUS_LENGTH_REQUIRED: return "LENGTH_REQUIRED";
+ case STATUS_PRECONDITION_FAILED: return "PRECONDITION_FAILED";
+ case STATUS_FILE_ALREADY_EXISTS_ERROR: return "FILE_ALREADY_EXISTS_ERROR";
+ case STATUS_CANNOT_RESUME: return "CANNOT_RESUME";
+ case STATUS_CANCELED: return "CANCELED";
+ case STATUS_UNKNOWN_ERROR: return "UNKNOWN_ERROR";
+ case STATUS_FILE_ERROR: return "FILE_ERROR";
+ case STATUS_UNHANDLED_REDIRECT: return "UNHANDLED_REDIRECT";
+ case STATUS_UNHANDLED_HTTP_CODE: return "UNHANDLED_HTTP_CODE";
+ case STATUS_HTTP_DATA_ERROR: return "HTTP_DATA_ERROR";
+ case STATUS_HTTP_EXCEPTION: return "HTTP_EXCEPTION";
+ case STATUS_TOO_MANY_REDIRECTS: return "TOO_MANY_REDIRECTS";
+ case STATUS_BLOCKED: return "BLOCKED";
+ default: return Integer.toString(status);
+ }
+ }
+
/**
* This download is visible but only shows in the notifications
* while it's in progress.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2aaf548..6dfbb2f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1464,6 +1464,20 @@
public static final String VIBRATE_ON = "vibrate_on";
/**
+ * If 1, redirects the system vibrator to all currently attached input devices
+ * that support vibration. If there are no such input devices, then the system
+ * vibrator is used instead.
+ * If 0, does not register the system vibrator.
+ *
+ * This setting is mainly intended to provide a compatibility mechanism for
+ * applications that only know about the system vibrator and do not use the
+ * input device vibrator API.
+ *
+ * @hide
+ */
+ public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*/
@@ -1970,6 +1984,7 @@
SCREEN_BRIGHTNESS_MODE,
SCREEN_AUTO_BRIGHTNESS_ADJ,
VIBRATE_ON,
+ VIBRATE_INPUT_DEVICES,
MODE_RINGER,
MODE_RINGER_STREAMS_AFFECTED,
MUTE_STREAMS_AFFECTED,
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 8c97293..35e2e4a 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -147,6 +147,15 @@
return out.toString();
}
+ /**
+ * Returns an HTML escaped representation of the given plain text.
+ */
+ public static String escapeHtml(CharSequence text) {
+ StringBuilder out = new StringBuilder();
+ withinStyle(out, text, 0, text.length());
+ return out.toString();
+ }
+
private static void withinHtml(StringBuilder out, Spanned text) {
int len = text.length();
@@ -370,7 +379,7 @@
}
}
- private static void withinStyle(StringBuilder out, Spanned text,
+ private static void withinStyle(StringBuilder out, CharSequence text,
int start, int end) {
for (int i = start; i < end; i++) {
char c = text.charAt(i);
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
new file mode 100644
index 0000000..ab21b32
--- /dev/null
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
+import android.util.SparseLongArray;
+import android.view.ViewGroup.ChildListForAccessibility;
+import android.view.accessibility.AccessibilityInteractionClient;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 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 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 ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+ new ArrayList<AccessibilityNodeInfo>();
+
+ private final Handler mHandler = new PrivateHandler();
+
+ private final ViewRootImpl mViewRootImpl;
+
+ private final AccessibilityNodePrefetcher mPrefetcher;
+
+ public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
+ mViewRootImpl = viewRootImpl;
+ mPrefetcher = new AccessibilityNodePrefetcher();
+ }
+
+ // 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)
+ );
+
+ private 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(
+ long accessibilityNodeId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+ long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+ message.arg1 = flags;
+ SomeArgs args = mPool.acquire();
+ args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi3 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interrogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+ final int flags = message.arg1;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int accessibilityViewId = args.argi1;
+ final int virtualDescendantId = args.argi2;
+ final int interactionId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+ List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View root = null;
+ if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+ root = mViewRootImpl.mView;
+ } else {
+ root = findViewByAccessibilityId(accessibilityViewId);
+ }
+ if (root != null && isDisplayedOnScreen(root)) {
+ mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+ infos.clear();
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
+ int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ int flags, int interrogatingPid, long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+ message.arg1 = flags;
+ message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ SomeArgs args = mPool.acquire();
+ args.argi1 = viewId;
+ args.argi2 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interrogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+ final int flags = message.arg1;
+ final int accessibilityViewId = message.arg2;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int viewId = args.argi1;
+ final int interactionId = args.argi2;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+ AccessibilityNodeInfo info = null;
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View root = null;
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ root = findViewByAccessibilityId(accessibilityViewId);
+ } else {
+ root = mViewRootImpl.mView;
+ }
+ if (root != null) {
+ View target = root.findViewById(viewId);
+ if (target != null && isDisplayedOnScreen(target)) {
+ info = target.createAccessibilityNodeInfo();
+ }
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
+ String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ int flags, int interrogatingPid, long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
+ message.arg1 = flags;
+ SomeArgs args = mPool.acquire();
+ args.arg1 = text;
+ args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi3 = interactionId;
+ args.arg2 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interrogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void findAccessibilityNodeInfosByTextUiThread(Message message) {
+ final int flags = message.arg1;
+ SomeArgs args = (SomeArgs) message.obj;
+ final String text = (String) args.arg1;
+ final int accessibilityViewId = args.argi1;
+ final int virtualDescendantId = args.argi2;
+ final int interactionId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg2;
+ mPool.release(args);
+ List<AccessibilityNodeInfo> infos = null;
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View root = null;
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ root = findViewByAccessibilityId(accessibilityViewId);
+ } else {
+ root = mViewRootImpl.mView;
+ }
+ if (root != null && isDisplayedOnScreen(root)) {
+ AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
+ if (provider != null) {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
+ ArrayList<View> foundViews = mViewRootImpl.mAttachInfo.mTempArrayList;
+ foundViews.clear();
+ root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
+ | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
+ | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
+ if (!foundViews.isEmpty()) {
+ infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
+ final int viewCount = foundViews.size();
+ for (int i = 0; i < viewCount; i++) {
+ View foundView = foundViews.get(i);
+ if (isDisplayedOnScreen(foundView)) {
+ provider = foundView.getAccessibilityNodeProvider();
+ if (provider != null) {
+ List<AccessibilityNodeInfo> infosFromProvider =
+ provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ if (infosFromProvider != null) {
+ infos.addAll(infosFromProvider);
+ }
+ } else {
+ infos.add(foundView.createAccessibilityNodeInfo());
+ }
+ }
+ }
+ }
+ }
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void findFocusClientThread(long accessibilityNodeId, int interactionId, int focusType,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+ long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_FIND_FOCUS;
+ message.arg1 = flags;
+ message.arg2 = focusType;
+ SomeArgs args = mPool.acquire();
+ args.argi1 = interactionId;
+ args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.arg1 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void findFocusUiThread(Message message) {
+ final int flags = message.arg1;
+ final int focusType = message.arg2;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int interactionId = args.argi1;
+ final int accessibilityViewId = args.argi2;
+ final int virtualDescendantId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+ AccessibilityNodeInfo focused = null;
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View root = null;
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ root = findViewByAccessibilityId(accessibilityViewId);
+ } else {
+ root = mViewRootImpl.mView;
+ }
+ if (root != null && isDisplayedOnScreen(root)) {
+ switch (focusType) {
+ case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
+ View host = mViewRootImpl.mAccessibilityFocusedHost;
+ // If there is no accessibility focus host or it is not a descendant
+ // of the root from which to start the search, then the search failed.
+ if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
+ break;
+ }
+ // If the host has a provider ask this provider to search for the
+ // focus instead fetching all provider nodes to do the search here.
+ AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
+ if (provider != null) {
+ focused = provider.findAccessibilitiyFocus(virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ focused = host.createAccessibilityNodeInfo();
+ }
+ } break;
+ case AccessibilityNodeInfo.FOCUS_INPUT: {
+ // Input focus cannot go to virtual views.
+ View target = root.findFocus();
+ if (target != null && isDisplayedOnScreen(target)) {
+ focused = target.createAccessibilityNodeInfo();
+ }
+ } break;
+ default:
+ throw new IllegalArgumentException("Unknown focus type: " + focusType);
+ }
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void focusSearchClientThread(long accessibilityNodeId, int interactionId, int direction,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+ long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_FOCUS_SEARCH;
+ message.arg1 = flags;
+ message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ SomeArgs args = mPool.acquire();
+ args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi2 = direction;
+ args.argi3 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void focusSearchUiThread(Message message) {
+ final int flags = message.arg1;
+ final int accessibilityViewId = message.arg2;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int virtualDescendantId = args.argi1;
+ final int direction = args.argi2;
+ final int interactionId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+ AccessibilityNodeInfo next = null;
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View root = null;
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ root = findViewByAccessibilityId(accessibilityViewId);
+ } else {
+ root = mViewRootImpl.mView;
+ }
+ if (root != null && isDisplayedOnScreen(root)) {
+ if ((direction & View.FOCUS_ACCESSIBILITY) == View.FOCUS_ACCESSIBILITY) {
+ AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
+ if (provider != null) {
+ next = provider.accessibilityFocusSearch(direction,
+ virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ View nextView = root.focusSearch(direction);
+ if (nextView != null) {
+ // If the focus search reached a node with a provider
+ // we delegate to the provider to find the next one.
+ provider = nextView.getAccessibilityNodeProvider();
+ if (provider != null) {
+ next = provider.accessibilityFocusSearch(direction,
+ virtualDescendantId);
+ } else {
+ next = nextView.createAccessibilityNodeInfo();
+ }
+ }
+ }
+ } else {
+ View nextView = root.focusSearch(direction);
+ if (nextView != null) {
+ next = nextView.createAccessibilityNodeInfo();
+ }
+ }
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setFindAccessibilityNodeInfoResult(next, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interogatingPid, long interrogatingTid) {
+ Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
+ message.arg1 = flags;
+ message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ SomeArgs args = mPool.acquire();
+ args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi2 = action;
+ args.argi3 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interogatingPid == Process.myPid()
+ && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
+ private void perfromAccessibilityActionUiThread(Message message) {
+ final int flags = message.arg1;
+ final int accessibilityViewId = message.arg2;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int virtualDescendantId = args.argi1;
+ final int action = args.argi2;
+ final int interactionId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+ boolean succeeded = false;
+ try {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ return;
+ }
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
+ (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ View target = null;
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ target = findViewByAccessibilityId(accessibilityViewId);
+ } else {
+ target = mViewRootImpl.mView;
+ }
+ if (target != null && isDisplayedOnScreen(target)) {
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ succeeded = provider.performAccessibilityAction(action, virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ succeeded = target.performAccessibilityAction(action);
+ }
+ }
+ } finally {
+ try {
+ mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ callback.setPerformAccessibilityActionResult(succeeded, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ private View findViewByAccessibilityId(int accessibilityId) {
+ View root = mViewRootImpl.mView;
+ if (root == null) {
+ return null;
+ }
+ View foundView = root.findViewByAccessibilityId(accessibilityId);
+ if (foundView != null && !isDisplayedOnScreen(foundView)) {
+ return null;
+ }
+ return foundView;
+ }
+
+ /**
+ * Computes whether a view is visible on the screen.
+ *
+ * @param view The view to check.
+ * @return Whether the view is visible on the screen.
+ */
+ private boolean isDisplayedOnScreen(View view) {
+ // The first two checks are made also made by isShown() which
+ // however traverses the tree up to the parent to catch that.
+ // Therefore, we do some fail fast check to minimize the up
+ // tree traversal.
+ return (view.mAttachInfo != null
+ && view.mAttachInfo.mWindowVisibility == View.VISIBLE
+ && view.isShown()
+ && view.getGlobalVisibleRect(mViewRootImpl.mTempRect));
+ }
+
+ /**
+ * This class encapsulates a prefetching strategy for the accessibility APIs for
+ * querying window content. It is responsible to prefetch a batch of
+ * AccessibilityNodeInfos in addition to the one for a requested node.
+ */
+ private class AccessibilityNodePrefetcher {
+
+ private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
+
+ public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
+ List<AccessibilityNodeInfo> outInfos) {
+ AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+ if (provider == null) {
+ AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
+ if (root != null) {
+ outInfos.add(root);
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ prefetchPredecessorsOfRealNode(view, outInfos);
+ }
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ prefetchSiblingsOfRealNode(view, outInfos);
+ }
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ prefetchDescendantsOfRealNode(view, outInfos);
+ }
+ }
+ } else {
+ AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
+ if (root != null) {
+ outInfos.add(root);
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
+ }
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
+ }
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ prefetchDescendantsOfVirtualNode(root, provider, outInfos);
+ }
+ }
+ }
+ }
+
+ private void prefetchPredecessorsOfRealNode(View view,
+ List<AccessibilityNodeInfo> outInfos) {
+ ViewParent parent = view.getParentForAccessibility();
+ while (parent instanceof View
+ && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ View parentView = (View) parent;
+ final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
+ parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
+ if (info != null) {
+ outInfos.add(info);
+ }
+ parent = parent.getParentForAccessibility();
+ }
+ }
+
+ private void prefetchSiblingsOfRealNode(View current,
+ List<AccessibilityNodeInfo> outInfos) {
+ ViewParent parent = current.getParentForAccessibility();
+ if (parent instanceof ViewGroup) {
+ ViewGroup parentGroup = (ViewGroup) parent;
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(parentGroup,
+ false);
+ final int childCount = children.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ children.recycle();
+ return;
+ }
+ View child = children.getChildAt(i);
+ if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
+ && isDisplayedOnScreen(child)) {
+ AccessibilityNodeInfo info = null;
+ AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+ if (provider == null) {
+ info = child.createAccessibilityNodeInfo();
+ } else {
+ info = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeInfo.UNDEFINED);
+ }
+ if (info != null) {
+ outInfos.add(info);
+ }
+ }
+ }
+ children.recycle();
+ }
+ }
+
+ private void prefetchDescendantsOfRealNode(View root,
+ List<AccessibilityNodeInfo> outInfos) {
+ if (!(root instanceof ViewGroup)) {
+ return;
+ }
+ ViewGroup rootGroup = (ViewGroup) root;
+ HashMap<View, AccessibilityNodeInfo> addedChildren =
+ new HashMap<View, AccessibilityNodeInfo>();
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(rootGroup, false);
+ final int childCount = children.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ children.recycle();
+ return;
+ }
+ View child = children.getChildAt(i);
+ if ( isDisplayedOnScreen(child)) {
+ AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+ if (provider == null) {
+ AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
+ if (info != null) {
+ outInfos.add(info);
+ addedChildren.put(child, null);
+ }
+ } else {
+ AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeInfo.UNDEFINED);
+ if (info != null) {
+ outInfos.add(info);
+ addedChildren.put(child, info);
+ }
+ }
+ }
+ }
+ children.recycle();
+ if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
+ View addedChild = entry.getKey();
+ AccessibilityNodeInfo virtualRoot = entry.getValue();
+ if (virtualRoot == null) {
+ prefetchDescendantsOfRealNode(addedChild, outInfos);
+ } else {
+ AccessibilityNodeProvider provider =
+ addedChild.getAccessibilityNodeProvider();
+ prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
+ }
+ }
+ }
+ }
+
+ private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
+ View providerHost, AccessibilityNodeProvider provider,
+ List<AccessibilityNodeInfo> outInfos) {
+ long parentNodeId = root.getParentNodeId();
+ int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+ while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ return;
+ }
+ final int virtualDescendantId =
+ AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ || accessibilityViewId == providerHost.getAccessibilityViewId()) {
+ AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
+ virtualDescendantId);
+ if (parent != null) {
+ outInfos.add(parent);
+ }
+ parentNodeId = parent.getParentNodeId();
+ accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
+ parentNodeId);
+ } else {
+ prefetchPredecessorsOfRealNode(providerHost, outInfos);
+ return;
+ }
+ }
+ }
+
+ private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
+ AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+ final long parentNodeId = current.getParentNodeId();
+ final int parentAccessibilityViewId =
+ AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+ final int parentVirtualDescendantId =
+ AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+ if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
+ AccessibilityNodeInfo parent =
+ provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+ if (parent != null) {
+ SparseLongArray childNodeIds = parent.getChildNodeIds();
+ final int childCount = childNodeIds.size();
+ for (int i = 0; i < childCount; i++) {
+ if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ return;
+ }
+ final long childNodeId = childNodeIds.get(i);
+ if (childNodeId != current.getSourceNodeId()) {
+ final int childVirtualDescendantId =
+ AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
+ AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+ childVirtualDescendantId);
+ if (child != null) {
+ outInfos.add(child);
+ }
+ }
+ }
+ }
+ } else {
+ prefetchSiblingsOfRealNode(providerHost, outInfos);
+ }
+ }
+
+ private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
+ AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+ SparseLongArray childNodeIds = root.getChildNodeIds();
+ final int initialOutInfosSize = outInfos.size();
+ final int childCount = childNodeIds.size();
+ for (int i = 0; i < childCount; i++) {
+ if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ return;
+ }
+ final long childNodeId = childNodeIds.get(i);
+ AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
+ if (child != null) {
+ outInfos.add(child);
+ }
+ }
+ if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ final int addedChildCount = outInfos.size() - initialOutInfosSize;
+ for (int i = 0; i < addedChildCount; i++) {
+ AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
+ prefetchDescendantsOfVirtualNode(child, provider, outInfos);
+ }
+ }
+ }
+ }
+
+ private class PrivateHandler extends Handler {
+ private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
+ private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
+ private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 3;
+ private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
+ private final static int MSG_FIND_FOCUS = 5;
+ private final static int MSG_FOCUS_SEARCH = 6;
+
+ public PrivateHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public String getMessageName(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MSG_PERFORM_ACCESSIBILITY_ACTION:
+ return "MSG_PERFORM_ACCESSIBILITY_ACTION";
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
+ return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
+ return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
+ return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
+ case MSG_FIND_FOCUS:
+ return "MSG_FIND_FOCUS";
+ case MSG_FOCUS_SEARCH:
+ return "MSG_FOCUS_SEARCH";
+ default:
+ throw new IllegalArgumentException("Unknown message type: " + type);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+ findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
+ } break;
+ case MSG_PERFORM_ACCESSIBILITY_ACTION: {
+ perfromAccessibilityActionUiThread(message);
+ } break;
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+ findAccessibilityNodeInfoByViewIdUiThread(message);
+ } break;
+ case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
+ findAccessibilityNodeInfosByTextUiThread(message);
+ } break;
+ case MSG_FIND_FOCUS: {
+ findFocusUiThread(message);
+ } break;
+ case MSG_FOCUS_SEARCH: {
+ focusSearchUiThread(message);
+ } break;
+ default:
+ throw new IllegalArgumentException("Unknown message type: " + type);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 3529b8e..8a01c15 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -17,10 +17,12 @@
package android.view;
import android.graphics.Rect;
+import android.view.ViewGroup.ChildListForAccessibility;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Stack;
/**
* The algorithm used for finding the next focusable view in a given direction
@@ -30,7 +32,7 @@
private static ThreadLocal<FocusFinder> tlFocusFinder =
new ThreadLocal<FocusFinder>() {
-
+ @Override
protected FocusFinder initialValue() {
return new FocusFinder();
}
@@ -48,6 +50,10 @@
Rect mBestCandidateRect = new Rect();
SequentialFocusComparator mSequentialFocusComparator = new SequentialFocusComparator();
+ private final ArrayList<View> mTempList = new ArrayList<View>();
+
+ private Stack<View> mTempStack;
+
// enforce thread local access
private FocusFinder() {}
@@ -60,7 +66,30 @@
* @return The next focusable view, or null if none exists.
*/
public final View findNextFocus(ViewGroup root, View focused, int direction) {
+ return findNextFocus(root, focused, mFocusedRect, direction);
+ }
+ /**
+ * Find the next view to take focus in root's descendants, searching from
+ * a particular rectangle in root's coordinates.
+ * @param root Contains focusedRect. Cannot be null.
+ * @param focusedRect The starting point of the search.
+ * @param direction Direction to look.
+ * @return The next focusable view, or null if none exists.
+ */
+ public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
+ return findNextFocus(root, null, focusedRect, direction);
+ }
+
+ private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
+ if ((direction & View.FOCUS_ACCESSIBILITY) != View.FOCUS_ACCESSIBILITY) {
+ return findNextInputFocus(root, focused, focusedRect, direction);
+ } else {
+ return findNextAccessibilityFocus(root, focused, direction);
+ }
+ }
+
+ private View findNextInputFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
if (focused != null) {
// check for user specified next focus
View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
@@ -79,90 +108,154 @@
switch (direction) {
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
- setFocusBottomRight(root);
+ setFocusTopLeft(root);
break;
case View.FOCUS_FORWARD:
if (root.isLayoutRtl()) {
- setFocusTopLeft(root);
- } else {
setFocusBottomRight(root);
+ } else {
+ setFocusTopLeft(root);
}
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
- setFocusTopLeft(root);
+ setFocusBottomRight(root);
break;
case View.FOCUS_BACKWARD:
if (root.isLayoutRtl()) {
- setFocusBottomRight(root);
- } else {
setFocusTopLeft(root);
+ } else {
+ setFocusBottomRight(root);
break;
}
}
}
- return findNextFocus(root, focused, mFocusedRect, direction);
+
+ ArrayList<View> focusables = mTempList;
+ focusables.clear();
+ root.addFocusables(focusables, direction);
+ if (focusables.isEmpty()) {
+ // The focus cannot change.
+ return null;
+ }
+
+ try {
+ switch (direction) {
+ case View.FOCUS_FORWARD:
+ case View.FOCUS_BACKWARD:
+ return findNextInputFocusInRelativeDirection(focusables, root, focused,
+ focusedRect, direction);
+ case View.FOCUS_UP:
+ case View.FOCUS_DOWN:
+ case View.FOCUS_LEFT:
+ case View.FOCUS_RIGHT:
+ return findNextInputFocusInAbsoluteDirection(focusables, root, focused,
+ focusedRect, direction);
+ default:
+ throw new IllegalArgumentException("Unknown direction: " + direction);
+ }
+ } finally {
+ focusables.clear();
+ }
}
- private void setFocusTopLeft(ViewGroup root) {
+ /**
+ * Find the next view to take accessibility focus in root's descendants,
+ * starting from the view that currently is accessibility focused.
+ *
+ * @param root The root which also contains the focused view.
+ * @param focused The current accessibility focused view.
+ * @param direction Direction to look.
+ * @return The next focusable view, or null if none exists.
+ */
+ private View findNextAccessibilityFocus(ViewGroup root, View focused, int direction) {
+ switch (direction) {
+ case View.ACCESSIBILITY_FOCUS_IN:
+ case View.ACCESSIBILITY_FOCUS_OUT:
+ case View.ACCESSIBILITY_FOCUS_FORWARD:
+ case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+ return findNextHierarchicalAcessibilityFocus(root, focused, direction);
+ }
+ case View.ACCESSIBILITY_FOCUS_LEFT:
+ case View.ACCESSIBILITY_FOCUS_RIGHT:
+ case View.ACCESSIBILITY_FOCUS_UP:
+ case View.ACCESSIBILITY_FOCUS_DOWN: {
+ return findNextDirectionalAccessibilityFocus(root, focused, direction);
+ }
+ default:
+ throw new IllegalArgumentException("Unknown direction: " + direction);
+ }
+ }
+
+ private View findNextHierarchicalAcessibilityFocus(ViewGroup root, View focused,
+ int direction) {
+ View current = (focused != null) ? focused : root;
+ switch (direction) {
+ case View.ACCESSIBILITY_FOCUS_IN: {
+ return findNextAccessibilityFocusIn(current);
+ }
+ case View.ACCESSIBILITY_FOCUS_OUT: {
+ return findNextAccessibilityFocusOut(current);
+ }
+ case View.ACCESSIBILITY_FOCUS_FORWARD: {
+ return findNextAccessibilityFocusForward(current);
+ }
+ case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+ return findNextAccessibilityFocusBackward(current);
+ }
+ }
+ return null;
+ }
+
+ private View findNextDirectionalAccessibilityFocus(ViewGroup root, View focused,
+ int direction) {
+ ArrayList<View> focusables = mTempList;
+ focusables.clear();
+ root.addFocusables(focusables, direction, View.FOCUSABLES_ACCESSIBILITY);
+ Rect focusedRect = getFocusedRect(root, focused, direction);
+ final int inputFocusDirection = getCorrespondingInputFocusDirection(direction);
+ View next = findNextInputFocusInAbsoluteDirection(focusables, root,
+ focused, focusedRect, inputFocusDirection);
+ focusables.clear();
+ return next;
+ }
+
+ private View findNextInputFocusInRelativeDirection(ArrayList<View> focusables, ViewGroup root,
+ View focused, Rect focusedRect, int direction) {
+ try {
+ // Note: This sort is stable.
+ mSequentialFocusComparator.setRoot(root);
+ Collections.sort(focusables, mSequentialFocusComparator);
+ } finally {
+ mSequentialFocusComparator.recycle();
+ }
+
+ final int count = focusables.size();
+ switch (direction) {
+ case View.FOCUS_FORWARD:
+ return getForwardFocusable(root, focused, focusables, count);
+ case View.FOCUS_BACKWARD:
+ return getBackwardFocusable(root, focused, focusables, count);
+ }
+ return focusables.get(count - 1);
+ }
+
+ private void setFocusBottomRight(ViewGroup root) {
final int rootBottom = root.getScrollY() + root.getHeight();
final int rootRight = root.getScrollX() + root.getWidth();
mFocusedRect.set(rootRight, rootBottom,
rootRight, rootBottom);
}
- private void setFocusBottomRight(ViewGroup root) {
+ private void setFocusTopLeft(ViewGroup root) {
final int rootTop = root.getScrollY();
final int rootLeft = root.getScrollX();
mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
}
- /**
- * Find the next view to take focus in root's descendants, searching from
- * a particular rectangle in root's coordinates.
- * @param root Contains focusedRect. Cannot be null.
- * @param focusedRect The starting point of the search.
- * @param direction Direction to look.
- * @return The next focusable view, or null if none exists.
- */
- public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
- return findNextFocus(root, null, focusedRect, direction);
- }
-
- private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
- ArrayList<View> focusables = root.getFocusables(direction);
- if (focusables.isEmpty()) {
- // The focus cannot change.
- return null;
- }
-
- if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
- if (focused != null && !focusables.contains(focused)) {
- // Add the currently focused view to the list to have it sorted
- // along with the other views.
- focusables.add(focused);
- }
-
- try {
- // Note: This sort is stable.
- mSequentialFocusComparator.setRoot(root);
- Collections.sort(focusables, mSequentialFocusComparator);
- } finally {
- mSequentialFocusComparator.recycle();
- }
-
- final int count = focusables.size();
- switch (direction) {
- case View.FOCUS_FORWARD:
- return getForwardFocusable(root, focused, focusables, count);
-
- case View.FOCUS_BACKWARD:
- return getBackwardFocusable(root, focused, focusables, count);
- }
- return null;
- }
-
+ View findNextInputFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
+ Rect focusedRect, int direction) {
// initialize the best candidate to something impossible
// (so the first plausible view will become the best choice)
mBestCandidateRect.set(focusedRect);
@@ -201,6 +294,140 @@
return closest;
}
+ private View findNextAccessibilityFocusIn(View view) {
+ // We have to traverse the full view tree to make sure
+ // we consider views in the order specified by their
+ // parent layout managers since some managers could be
+ // LTR while some could be RTL.
+ if (mTempStack == null) {
+ mTempStack = new Stack<View>();
+ }
+ Stack<View> fringe = mTempStack;
+ fringe.clear();
+ fringe.add(view);
+ while (!fringe.isEmpty()) {
+ View current = fringe.pop();
+ if (current.getAccessibilityNodeProvider() != null) {
+ fringe.clear();
+ return current;
+ }
+ if (current != view && current.includeForAccessibility()) {
+ fringe.clear();
+ return current;
+ }
+ if (current instanceof ViewGroup) {
+ ViewGroup currentGroup = (ViewGroup) current;
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(
+ currentGroup, true);
+ final int childCount = children.getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ fringe.push(children.getChildAt(i));
+ }
+ children.recycle();
+ }
+ }
+ return null;
+ }
+
+ private View findNextAccessibilityFocusOut(View view) {
+ ViewParent parent = view.getParentForAccessibility();
+ if (parent instanceof View) {
+ return (View) parent;
+ }
+ return null;
+ }
+
+ private View findNextAccessibilityFocusForward(View view) {
+ // We have to traverse the full view tree to make sure
+ // we consider views in the order specified by their
+ // parent layout managers since some managers could be
+ // LTR while some could be RTL.
+ View current = view;
+ while (current != null) {
+ ViewParent parent = current.getParent();
+ if (!(parent instanceof ViewGroup)) {
+ return null;
+ }
+ ViewGroup parentGroup = (ViewGroup) parent;
+ // Ask the parent to find a sibling after the current view
+ // that can take accessibility focus.
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(
+ parentGroup, true);
+ final int fromIndex = children.getChildIndex(current) + 1;
+ final int childCount = children.getChildCount();
+ for (int i = fromIndex; i < childCount; i++) {
+ View child = children.getChildAt(i);
+ View next = null;
+ if (child.getAccessibilityNodeProvider() != null) {
+ next = child;
+ } else if (child.includeForAccessibility()) {
+ next = child;
+ } else {
+ next = findNextAccessibilityFocusIn(child);
+ }
+ if (next != null) {
+ children.recycle();
+ return next;
+ }
+ }
+ children.recycle();
+ // Reaching a regarded for accessibility predecessor without
+ // finding a next view to take focus means that at this level
+ // there is no next accessibility focusable sibling.
+ if (parentGroup.includeForAccessibility()) {
+ return null;
+ }
+ // Try asking a predecessor to find a focusable.
+ current = parentGroup;
+ }
+ return null;
+ }
+
+ private View findNextAccessibilityFocusBackward(View view) {
+ // We have to traverse the full view tree to make sure
+ // we consider views in the order specified by their
+ // parent layout managers since some managers could be
+ // LTR while some could be RTL.
+ View current = view;
+ while (current != null) {
+ ViewParent parent = current.getParent();
+ if (!(parent instanceof ViewGroup)) {
+ return null;
+ }
+ ViewGroup parentGroup = (ViewGroup) parent;
+ // Ask the parent to find a sibling after the current view
+ // to take accessibility focus
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(
+ parentGroup, true);
+ final int fromIndex = children.getChildIndex(current) - 1;
+ for (int i = fromIndex; i >= 0; i--) {
+ View child = children.getChildAt(i);
+ View next = null;
+ if (child.getAccessibilityNodeProvider() != null) {
+ next = child;
+ } else if (child.includeForAccessibility()) {
+ next = child;
+ } else {
+ next = findNextAccessibilityFocusIn(child);
+ }
+ if (next != null) {
+ children.recycle();
+ return next;
+ }
+ }
+ children.recycle();
+ // Reaching a regarded for accessibility predecessor without
+ // finding a previous view to take focus means that at this level
+ // there is no previous accessibility focusable sibling.
+ if (parentGroup.includeForAccessibility()) {
+ return null;
+ }
+ // Try asking a predecessor to find a focusable.
+ current = parentGroup;
+ }
+ return null;
+ }
+
private static View getForwardFocusable(ViewGroup root, View focused,
ArrayList<View> focusables, int count) {
return (root.isLayoutRtl()) ?
@@ -235,6 +462,47 @@
return focusables.get(count - 1);
}
+ private Rect getFocusedRect(ViewGroup root, View focused, int direction) {
+ Rect focusedRect = mFocusedRect;
+ if (focused != null) {
+ focused.getFocusedRect(focusedRect);
+ root.offsetDescendantRectToMyCoords(focused, focusedRect);
+ } else {
+ switch (direction) {
+ case View.FOCUS_RIGHT:
+ case View.FOCUS_DOWN:
+ final int rootTop = root.getScrollY();
+ final int rootLeft = root.getScrollX();
+ focusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ break;
+
+ case View.FOCUS_LEFT:
+ case View.FOCUS_UP:
+ final int rootBottom = root.getScrollY() + root.getHeight();
+ final int rootRight = root.getScrollX() + root.getWidth();
+ focusedRect.set(rootRight, rootBottom, rootRight, rootBottom);
+ break;
+ }
+ }
+ return focusedRect;
+ }
+
+ private int getCorrespondingInputFocusDirection(int accessFocusDirection) {
+ switch (accessFocusDirection) {
+ case View.ACCESSIBILITY_FOCUS_LEFT:
+ return View.FOCUS_LEFT;
+ case View.ACCESSIBILITY_FOCUS_RIGHT:
+ return View.FOCUS_RIGHT;
+ case View.ACCESSIBILITY_FOCUS_UP:
+ return View.FOCUS_UP;
+ case View.ACCESSIBILITY_FOCUS_DOWN:
+ return View.FOCUS_DOWN;
+ default:
+ throw new IllegalArgumentException("Cannot map accessiblity focus"
+ + " direction: " + accessFocusDirection);
+ }
+ }
+
/**
* Is rect1 a better candidate than rect2 for a focus search in a particular
* direction from a source rect? This is the core routine that determines
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8fe8e40..b70d7b5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -80,6 +80,8 @@
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
int getPendingAppTransition();
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
+ void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+ int startHeight);
void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback);
void executeAppTransition();
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 75b2c746..4848a7a 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,9 +16,12 @@
package android.view;
+import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Vibrator;
+import android.os.NullVibrator;
import java.util.ArrayList;
import java.util.List;
@@ -40,13 +43,17 @@
*/
public final class InputDevice implements Parcelable {
private final int mId;
+ private final int mGeneration;
private final String mName;
private final String mDescriptor;
private final int mSources;
private final int mKeyboardType;
private final KeyCharacterMap mKeyCharacterMap;
+ private final boolean mHasVibrator;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
+ private Vibrator mVibrator; // guarded by mMotionRanges during initialization
+
/**
* A mask for input source classes.
*
@@ -302,23 +309,27 @@
};
// Called by native code.
- private InputDevice(int id, String name, String descriptor, int sources,
- int keyboardType, KeyCharacterMap keyCharacterMap) {
+ private InputDevice(int id, int generation, String name, String descriptor, int sources,
+ int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator) {
mId = id;
+ mGeneration = generation;
mName = name;
mDescriptor = descriptor;
mSources = sources;
mKeyboardType = keyboardType;
mKeyCharacterMap = keyCharacterMap;
+ mHasVibrator = hasVibrator;
}
private InputDevice(Parcel in) {
mId = in.readInt();
+ mGeneration = in.readInt();
mName = in.readString();
mDescriptor = in.readString();
mSources = in.readInt();
mKeyboardType = in.readInt();
mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
+ mHasVibrator = in.readInt() != 0;
for (;;) {
int axis = in.readInt();
@@ -364,6 +375,19 @@
}
/**
+ * Gets a generation number for this input device.
+ * The generation number is incremented whenever the device is reconfigured and its
+ * properties may have changed.
+ *
+ * @return The generation number.
+ *
+ * @hide
+ */
+ public int getGeneration() {
+ return mGeneration;
+ }
+
+ /**
* Gets the input device descriptor, which is a stable identifier for an input device.
* <p>
* An input device descriptor uniquely identifies an input device. Its value
@@ -506,6 +530,31 @@
}
/**
+ * Gets the vibrator service associated with the device, if there is one.
+ * Even if the device does not have a vibrator, the result is never null.
+ * Use {@link Vibrator#hasVibrator} to determine whether a vibrator is
+ * present.
+ *
+ * Note that the vibrator associated with the device may be different from
+ * the system vibrator. To obtain an instance of the system vibrator instead, call
+ * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
+ *
+ * @return The vibrator service associated with the device, never null.
+ */
+ public Vibrator getVibrator() {
+ synchronized (mMotionRanges) {
+ if (mVibrator == null) {
+ if (mHasVibrator) {
+ mVibrator = InputManager.getInstance().getInputDeviceVibrator(mId);
+ } else {
+ mVibrator = NullVibrator.getInstance();
+ }
+ }
+ return mVibrator;
+ }
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
@@ -595,11 +644,13 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mId);
+ out.writeInt(mGeneration);
out.writeString(mName);
out.writeString(mDescriptor);
out.writeInt(mSources);
out.writeInt(mKeyboardType);
mKeyCharacterMap.writeToParcel(out, flags);
+ out.writeInt(mHasVibrator ? 1 : 0);
final int numRanges = mMotionRanges.size();
for (int i = 0; i < numRanges; i++) {
@@ -624,6 +675,7 @@
StringBuilder description = new StringBuilder();
description.append("Input Device ").append(mId).append(": ").append(mName).append("\n");
description.append(" Descriptor: ").append(mDescriptor).append("\n");
+ description.append(" Generation: ").append(mGeneration).append("\n");
description.append(" Keyboard Type: ");
switch (mKeyboardType) {
@@ -639,6 +691,8 @@
}
description.append("\n");
+ description.append(" Has Vibrator: ").append(mHasVibrator).append("\n");
+
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 3d165ea..12d7b12 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -22,8 +22,6 @@
import android.util.AndroidRuntimeException;
import android.util.SparseIntArray;
import android.hardware.input.InputManager;
-import android.util.SparseArray;
-import android.view.InputDevice.MotionRange;
import java.lang.Character;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 32029ba..214dc5c 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -676,7 +676,6 @@
*
* @param surfaceTexture The {@link SurfaceTexture} that the view should use.
* @see SurfaceTexture#detachFromGLContext()
- * @hide
*/
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
if (surfaceTexture == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1fa19d1..0ded5f9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24,6 +24,7 @@
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
@@ -975,6 +976,14 @@
public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
/**
+ * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
+ * should add only accessibility focusable Views.
+ *
+ * @hide
+ */
+ public static final int FOCUSABLES_ACCESSIBILITY = 0x00000002;
+
+ /**
* Use with {@link #focusSearch(int)}. Move focus to the previous selectable
* item.
*/
@@ -1006,6 +1015,54 @@
*/
public static final int FOCUS_DOWN = 0x00000082;
+ // Accessibility focus directions.
+
+ /**
+ * The accessibility focus which is the current user position when
+ * interacting with the accessibility framework.
+ */
+ public static final int FOCUS_ACCESSIBILITY = 0x00001000;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus left.
+ */
+ public static final int ACCESSIBILITY_FOCUS_LEFT = FOCUS_LEFT | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus up.
+ */
+ public static final int ACCESSIBILITY_FOCUS_UP = FOCUS_UP | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus right.
+ */
+ public static final int ACCESSIBILITY_FOCUS_RIGHT = FOCUS_RIGHT | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus down.
+ */
+ public static final int ACCESSIBILITY_FOCUS_DOWN = FOCUS_DOWN | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus to the next view.
+ */
+ public static final int ACCESSIBILITY_FOCUS_FORWARD = FOCUS_FORWARD | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus to the previous view.
+ */
+ public static final int ACCESSIBILITY_FOCUS_BACKWARD = FOCUS_BACKWARD | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus in a view.
+ */
+ public static final int ACCESSIBILITY_FOCUS_IN = 0x00000004 | FOCUS_ACCESSIBILITY;
+
+ /**
+ * Use with {@link #focusSearch(int)}. Move acessibility focus out of a view.
+ */
+ public static final int ACCESSIBILITY_FOCUS_OUT = 0x00000008 | FOCUS_ACCESSIBILITY;
+
/**
* Bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that provide the actual measured size.
@@ -1330,7 +1387,7 @@
R.attr.state_accelerated, VIEW_STATE_ACCELERATED,
R.attr.state_hovered, VIEW_STATE_HOVERED,
R.attr.state_drag_can_accept, VIEW_STATE_DRAG_CAN_ACCEPT,
- R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED,
+ R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED
};
static {
@@ -1452,7 +1509,8 @@
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
| AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
| AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
- | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED;
+ | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
/**
* Temporary Rect currently for use in setBackground(). This will probably
@@ -1784,7 +1842,6 @@
*/
private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
-
/**
* Indicates that the view is tracking some sort of transient state
* that the app should not need to be aware of, but that the framework
@@ -1992,6 +2049,50 @@
public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+ // Accessiblity constants for mPrivateFlags2
+
+ /**
+ * Shift for accessibility related bits in {@link #mPrivateFlags2}.
+ */
+ static final int IMPORTANT_FOR_ACCESSIBILITY_SHIFT = 20;
+
+ /**
+ * Automatically determine whether a view is important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000;
+
+ /**
+ * The view is important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001;
+
+ /**
+ * The view is not important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002;
+
+ /**
+ * The default whether the view is important for accessiblity.
+ */
+ static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+
+ /**
+ * Mask for obtainig the bits which specify how to determine
+ * whether a view is important for accessibility.
+ */
+ static final int IMPORTANT_FOR_ACCESSIBILITY_MASK = (IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ | IMPORTANT_FOR_ACCESSIBILITY_YES | IMPORTANT_FOR_ACCESSIBILITY_NO)
+ << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+
+ /**
+ * Flag indicating whether a view has accessibility focus.
+ */
+ static final int ACCESSIBILITY_FOCUSED = 0x00000040 << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+
+ /**
+ * Flag indicating whether a view state for accessibility has changed.
+ */
+ static final int ACCESSIBILITY_STATE_CHANGED = 0x00000080 << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
/* End of masks for mPrivateFlags2 */
@@ -2598,6 +2699,12 @@
protected int mPaddingBottom;
/**
+ * The layout insets in pixels, that is the distance in pixels between the
+ * visible edges of this view its bounds.
+ */
+ private Insets mLayoutInsets;
+
+ /**
* Briefly describes the view and is primarily used for accessibility support.
*/
private CharSequence mContentDescription;
@@ -2952,7 +3059,8 @@
// Set layout and text direction defaults
mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
(TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
- (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
+ (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT) |
+ (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = -1;
@@ -3340,6 +3448,9 @@
final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
break;
+ case R.styleable.View_importantForAccessibility:
+ setImportantForAccessibility(a.getInt(attr,
+ IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
}
}
@@ -3970,6 +4081,10 @@
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
+
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ notifyAccessibilityStateChanged();
+ }
}
}
@@ -4050,16 +4165,21 @@
}
onFocusChanged(false, 0, null);
+
refreshDrawableState();
ensureInputFocusOnFirstFocusable();
+
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ notifyAccessibilityStateChanged();
+ }
}
}
void ensureInputFocusOnFirstFocusable() {
View root = getRootView();
if (root != null) {
- root.requestFocus(FOCUS_FORWARD);
+ root.requestFocus();
}
}
@@ -4077,6 +4197,10 @@
onFocusChanged(false, 0, null);
refreshDrawableState();
+
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ notifyAccessibilityStateChanged();
+ }
}
}
@@ -4127,7 +4251,10 @@
*/
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
if (gainFocus) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ requestAccessibilityFocus();
+ }
}
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4237,7 +4364,7 @@
*/
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
if (mAccessibilityDelegate != null) {
- mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
+ mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
} else {
sendAccessibilityEventUncheckedInternal(event);
}
@@ -4257,6 +4384,31 @@
if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
dispatchPopulateAccessibilityEvent(event);
}
+ // Intercept accessibility focus events fired by virtual nodes to keep
+ // track of accessibility focus position in such nodes.
+ final int eventType = event.getEventType();
+ switch (eventType) {
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
+ final long virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+ event.getSourceNodeId());
+ if (virtualNodeId != AccessibilityNodeInfo.UNDEFINED) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.setAccessibilityFocusedHost(this);
+ }
+ }
+ } break;
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
+ final long virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+ event.getSourceNodeId());
+ if (virtualNodeId != AccessibilityNodeInfo.UNDEFINED) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.setAccessibilityFocusedHost(null);
+ }
+ }
+ } break;
+ }
// In the beginning we called #isShown(), so we know that getParent() is not null.
getParent().requestSendAccessibilityEvent(this, event);
}
@@ -4399,7 +4551,7 @@
event.setContentDescription(mContentDescription);
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
- ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
+ ArrayList<View> focusablesTempList = mAttachInfo.mTempArrayList;
getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD,
FOCUSABLES_ALL);
event.setItemCount(focusablesTempList.size());
@@ -4488,10 +4640,9 @@
info.setBoundsInScreen(bounds);
if ((mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
- ViewParent parent = getParent();
+ ViewParent parent = getParentForAccessibility();
if (parent instanceof View) {
- View parentView = (View) parent;
- info.setParent(parentView);
+ info.setParent((View) parent);
}
}
@@ -4503,6 +4654,7 @@
info.setClickable(isClickable());
info.setFocusable(isFocusable());
info.setFocused(isFocused());
+ info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
info.setLongClickable(isLongClickable());
@@ -4597,10 +4749,11 @@
* true for views that do not have textual representation (For example,
* ImageButton).
*
- * @return The content descriptiopn.
+ * @return The content description.
*
* @attr ref android.R.styleable#View_contentDescription
*/
+ @ViewDebug.ExportedProperty(category = "accessibility")
public CharSequence getContentDescription() {
return mContentDescription;
}
@@ -5650,8 +5803,9 @@
* Adds any focusable views that are descendants of this view (possibly
* including this view if it is focusable itself) to views. This method
* adds all focusable views regardless if we are in touch mode or
- * only views focusable in touch mode if we are in touch mode depending on
- * the focusable mode paramater.
+ * only views focusable in touch mode if we are in touch mode or
+ * only views that can take accessibility focus if accessibility is enabeld
+ * depending on the focusable mode paramater.
*
* @param views Focusable views found so far or null if all we are interested is
* the number of focusables.
@@ -5662,18 +5816,24 @@
* @see #FOCUSABLES_TOUCH_MODE
*/
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (views == null) {
+ return;
+ }
+ if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()
+ && includeForAccessibility()) {
+ views.add(this);
+ return;
+ }
+ }
if (!isFocusable()) {
return;
}
-
- if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
- isInTouchMode() && !isFocusableInTouchMode()) {
+ if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
+ && isInTouchMode() && !isFocusableInTouchMode()) {
return;
}
-
- if (views != null) {
- views.add(this);
- }
+ views.add(this);
}
/**
@@ -5734,6 +5894,149 @@
}
/**
+ * Returns whether this View is accessibility focused.
+ *
+ * @return True if this View is accessibility focused.
+ */
+ boolean isAccessibilityFocused() {
+ return (mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0;
+ }
+
+ /**
+ * Call this to try to give accessibility focus to this view.
+ *
+ * A view will not actually take focus if {@link AccessibilityManager#isEnabled()}
+ * returns false or the view is no visible or the view already has accessibility
+ * focus.
+ *
+ * See also {@link #focusSearch(int)}, which is what you call to say that you
+ * have focus, and you want your parent to look for the next one.
+ *
+ * @return Whether this view actually took accessibility focus.
+ *
+ * @hide
+ */
+ public boolean requestAccessibilityFocus() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return false;
+ }
+ if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+ return false;
+ }
+ if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) == 0) {
+ mPrivateFlags2 |= ACCESSIBILITY_FOCUSED;
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.setAccessibilityFocusedHost(this);
+ }
+ invalidate();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ notifyAccessibilityStateChanged();
+ // Try to give input focus to this view - not a descendant.
+ requestFocusNoSearch(View.FOCUS_DOWN, null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Call this to try to clear accessibility focus of this view.
+ *
+ * See also {@link #focusSearch(int)}, which is what you call to say that you
+ * have focus, and you want your parent to look for the next one.
+ *
+ * @hide
+ */
+ public void clearAccessibilityFocus() {
+ if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
+ mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.setAccessibilityFocusedHost(null);
+ }
+ invalidate();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ notifyAccessibilityStateChanged();
+ // Try to move accessibility focus to the input focus.
+ View rootView = getRootView();
+ if (rootView != null) {
+ View inputFocus = rootView.findFocus();
+ if (inputFocus != null) {
+ inputFocus.requestAccessibilityFocus();
+ }
+ }
+ }
+ }
+
+ /**
+ * Find the best view to take accessibility focus from a hover.
+ * This function finds the deepest actionable view and if that
+ * fails ask the parent to take accessibility focus from hover.
+ *
+ * @param x The X hovered location in this view coorditantes.
+ * @param y The Y hovered location in this view coorditantes.
+ * @return Whether the request was handled.
+ *
+ * @hide
+ */
+ public boolean requestAccessibilityFocusFromHover(float x, float y) {
+ if (onRequestAccessibilityFocusFromHover(x, y)) {
+ return true;
+ }
+ ViewParent parent = mParent;
+ if (parent instanceof View) {
+ View parentView = (View) parent;
+
+ float[] position = mAttachInfo.mTmpTransformLocation;
+ position[0] = x;
+ position[1] = y;
+
+ // Compensate for the transformation of the current matrix.
+ if (!hasIdentityMatrix()) {
+ getMatrix().mapPoints(position);
+ }
+
+ // Compensate for the parent scroll and the offset
+ // of this view stop from the parent top.
+ position[0] += mLeft - parentView.mScrollX;
+ position[1] += mTop - parentView.mScrollY;
+
+ return parentView.requestAccessibilityFocusFromHover(position[0], position[1]);
+ }
+ return false;
+ }
+
+ /**
+ * Requests to give this View focus from hover.
+ *
+ * @param x The X hovered location in this view coorditantes.
+ * @param y The Y hovered location in this view coorditantes.
+ * @return Whether the request was handled.
+ *
+ * @hide
+ */
+ public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
+ if (includeForAccessibility()
+ && (isActionableForAccessibility() || hasListenersForAccessibility())) {
+ return requestAccessibilityFocus();
+ }
+ return false;
+ }
+
+ /**
+ * Clears accessibility focus without calling any callback methods
+ * normally invoked in {@link #clearAccessibilityFocus()}. This method
+ * is used for clearing accessibility focus when giving this focus to
+ * another view.
+ */
+ void clearAccessibilityFocusNoCallbacks() {
+ if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
+ mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+ invalidate();
+ }
+ }
+
+ /**
* Call this to try to give focus to a specific view or to one of its
* descendants.
*
@@ -5753,7 +6056,6 @@
return requestFocus(View.FOCUS_DOWN);
}
-
/**
* Call this to try to give focus to a specific view or to one of its
* descendants and give it a hint about what direction focus is heading.
@@ -5805,6 +6107,10 @@
* @return Whether this view or one of its descendants actually took focus.
*/
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ return requestFocusNoSearch(direction, previouslyFocusedRect);
+ }
+
+ private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// need to be focusable
if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
(mViewFlags & VISIBILITY_MASK) != VISIBLE) {
@@ -5864,6 +6170,248 @@
}
/**
+ * Gets the mode for determining whether this View is important for accessibility
+ * which is if it fires accessibility events and if it is reported to
+ * accessibility services that query the screen.
+ *
+ * @return The mode for determining whether a View is important for accessibility.
+ *
+ * @attr ref android.R.styleable#View_importantForAccessibility
+ *
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ */
+ @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO,
+ to = "IMPORTANT_FOR_ACCESSIBILITY_AUTO"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES,
+ to = "IMPORTANT_FOR_ACCESSIBILITY_YES"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO,
+ to = "IMPORTANT_FOR_ACCESSIBILITY_NO")
+ })
+ public int getImportantForAccessibility() {
+ return (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
+ >> IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ }
+
+ /**
+ * Sets how to determine whether this view is important for accessibility
+ * which is if it fires accessibility events and if it is reported to
+ * accessibility services that query the screen.
+ *
+ * @param mode How to determine whether this view is important for accessibility.
+ *
+ * @attr ref android.R.styleable#View_importantForAccessibility
+ *
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ */
+ public void setImportantForAccessibility(int mode) {
+ if (mode != getImportantForAccessibility()) {
+ mPrivateFlags2 &= ~IMPORTANT_FOR_ACCESSIBILITY_MASK;
+ mPrivateFlags2 |= (mode << IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
+ & IMPORTANT_FOR_ACCESSIBILITY_MASK;
+ notifyAccessibilityStateChanged();
+ }
+ }
+
+ /**
+ * Gets whether this view should be exposed for accessibility.
+ *
+ * @return Whether the view is exposed for accessibility.
+ *
+ * @hide
+ */
+ public boolean isImportantForAccessibility() {
+ final int mode = (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
+ >> IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ switch (mode) {
+ case IMPORTANT_FOR_ACCESSIBILITY_YES:
+ return true;
+ case IMPORTANT_FOR_ACCESSIBILITY_NO:
+ return false;
+ case IMPORTANT_FOR_ACCESSIBILITY_AUTO:
+ return isActionableForAccessibility() || hasListenersForAccessibility();
+ default:
+ throw new IllegalArgumentException("Unknow important for accessibility mode: "
+ + mode);
+ }
+ }
+
+ /**
+ * Gets the parent for accessibility purposes. Note that the parent for
+ * accessibility is not necessary the immediate parent. It is the first
+ * predecessor that is important for accessibility.
+ *
+ * @return The parent for accessibility purposes.
+ */
+ public ViewParent getParentForAccessibility() {
+ if (mParent instanceof View) {
+ View parentView = (View) mParent;
+ if (parentView.includeForAccessibility()) {
+ return mParent;
+ } else {
+ return mParent.getParentForAccessibility();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds the children of a given View for accessibility. Since some Views are
+ * not important for accessibility the children for accessibility are not
+ * necessarily direct children of the riew, rather they are the first level of
+ * descendants important for accessibility.
+ *
+ * @param children The list of children for accessibility.
+ */
+ public void addChildrenForAccessibility(ArrayList<View> children) {
+ if (includeForAccessibility()) {
+ children.add(this);
+ }
+ }
+
+ /**
+ * Whether to regard this view for accessibility. A view is regarded for
+ * accessibility if it is important for accessibility or the querying
+ * accessibility service has explicitly requested that view not
+ * important for accessibility are regarded.
+ *
+ * @return Whether to regard the view for accessibility.
+ */
+ boolean includeForAccessibility() {
+ if (mAttachInfo != null) {
+ if (!mAttachInfo.mIncludeNotImportantViews) {
+ return isImportantForAccessibility();
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the View is considered actionable from
+ * accessibility perspective. Such view are important for
+ * accessiiblity.
+ *
+ * @return True if the view is actionable for accessibility.
+ */
+ private boolean isActionableForAccessibility() {
+ return (isClickable() || isLongClickable() || isFocusable());
+ }
+
+ /**
+ * Returns whether the View has registered callbacks wich makes it
+ * important for accessiiblity.
+ *
+ * @return True if the view is actionable for accessibility.
+ */
+ private boolean hasListenersForAccessibility() {
+ ListenerInfo info = getListenerInfo();
+ return mTouchDelegate != null || info.mOnKeyListener != null
+ || info.mOnTouchListener != null || info.mOnGenericMotionListener != null
+ || info.mOnHoverListener != null || info.mOnDragListener != null;
+ }
+
+ /**
+ * Notifies accessibility services that some view's important for
+ * accessibility state has changed. Note that such notifications
+ * are made at most once every
+ * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
+ * to avoid unnecessary load to the system. Also once a view has
+ * made a notifucation this method is a NOP until the notification has
+ * been sent to clients.
+ *
+ * @hide
+ *
+ * TODO: Makse sure this method is called for any view state change
+ * that is interesting for accessilility purposes.
+ */
+ public void notifyAccessibilityStateChanged() {
+ if ((mPrivateFlags2 & ACCESSIBILITY_STATE_CHANGED) == 0) {
+ mPrivateFlags2 |= ACCESSIBILITY_STATE_CHANGED;
+ if (mParent != null) {
+ mParent.childAccessibilityStateChanged(this);
+ }
+ }
+ }
+
+ /**
+ * Reset the state indicating the this view has requested clients
+ * interested in its accessiblity state to be notified.
+ *
+ * @hide
+ */
+ public void resetAccessibilityStateChanged() {
+ mPrivateFlags2 &= ~ACCESSIBILITY_STATE_CHANGED;
+ }
+
+ /**
+ * Performs the specified accessibility action on the view. For
+ * possible accessibility actions look at {@link AccessibilityNodeInfo}.
+ *
+ * @param action The action to perform.
+ * @return Whether the action was performed.
+ */
+ public boolean performAccessibilityAction(int action) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ final long now = SystemClock.uptimeMillis();
+ // Send down.
+ MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+ getWidth() / 2, getHeight() / 2, 0);
+ onTouchEvent(event);
+ // Send up.
+ event.setAction(MotionEvent.ACTION_UP);
+ onTouchEvent(event);
+ // Clean up.
+ event.recycle();
+ } break;
+ case AccessibilityNodeInfo.ACTION_FOCUS: {
+ if (!hasFocus()) {
+ // Get out of touch mode since accessibility
+ // wants to move focus around.
+ getViewRootImpl().ensureTouchMode(false);
+ return requestFocus();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+ if (hasFocus()) {
+ clearFocus();
+ return !isFocused();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_SELECT: {
+ if (!isSelected()) {
+ setSelected(true);
+ return isSelected();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+ if (isSelected()) {
+ setSelected(false);
+ return !isSelected();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (!isAccessibilityFocused()) {
+ return requestAccessibilityFocus();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (isAccessibilityFocused()) {
+ clearAccessibilityFocus();
+ return true;
+ }
+ } break;
+ }
+ return false;
+ }
+
+ /**
* @hide
*/
public void dispatchStartTemporaryDetach() {
@@ -6757,21 +7305,27 @@
// The root view may receive hover (or touch) events that are outside the bounds of
// the window. This code ensures that we only send accessibility events for
// hovers that are actually within the bounds of the root view.
- final int action = event.getAction();
+ final int action = event.getActionMasked();
if (!mSendingHoverAccessibilityEvents) {
if ((action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE)
&& !hasHoveredChild()
&& pointInView(event.getX(), event.getY())) {
- mSendingHoverAccessibilityEvents = true;
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ mSendingHoverAccessibilityEvents = true;
+ requestAccessibilityFocusFromHover((int) event.getX(), (int) event.getY());
}
} else {
if (action == MotionEvent.ACTION_HOVER_EXIT
- || (action == MotionEvent.ACTION_HOVER_MOVE
+ || (action == MotionEvent.ACTION_MOVE
&& !pointInView(event.getX(), event.getY()))) {
mSendingHoverAccessibilityEvents = false;
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ // If the window does not have input focus we take away accessibility
+ // focus as soon as the user stop hovering over the view.
+ if (!mAttachInfo.mHasWindowFocus) {
+ getViewRootImpl().setAccessibilityFocusedHost(null);
+ }
}
}
@@ -6795,6 +7349,7 @@
dispatchGenericMotionEventInternal(event);
return true;
}
+
return false;
}
@@ -6806,7 +7361,6 @@
*/
private boolean isHoverable() {
final int viewFlags = mViewFlags;
- //noinspection SimplifiableIfStatement
if ((viewFlags & ENABLED_MASK) == DISABLED) {
return false;
}
@@ -7130,6 +7684,9 @@
*/
if (mParent != null) mParent.focusableViewAvailable(this);
}
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ notifyAccessibilityStateChanged();
+ }
}
if ((flags & VISIBILITY_MASK) == VISIBLE) {
@@ -7161,6 +7718,7 @@
if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
if (hasFocus()) clearFocus();
+ clearAccessibilityFocus();
destroyDrawingCache();
if (mParent instanceof View) {
// GONE views noop invalidation, so invalidate the parent
@@ -7185,9 +7743,10 @@
mPrivateFlags |= DRAWN;
if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {
- // root view becoming invisible shouldn't clear focus
+ // root view becoming invisible shouldn't clear focus and accessibility focus
if (getRootView() != this) {
clearFocus();
+ clearAccessibilityFocus();
}
}
if (mAttachInfo != null) {
@@ -7241,6 +7800,12 @@
mParent.recomputeViewAttributes(this);
}
}
+
+ if (AccessibilityManager.getInstance(mContext).isEnabled()
+ && ((changed & FOCUSABLE) != 0 || (changed & CLICKABLE) != 0
+ || (changed & LONG_CLICKABLE) != 0 || (changed & ENABLED) != 0)) {
+ notifyAccessibilityStateChanged();
+ }
}
/**
@@ -7319,6 +7884,7 @@
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(Canvas canvas) {
+
}
/**
@@ -10227,6 +10793,7 @@
resolvePadding();
resolveTextDirection();
resolveTextAlignment();
+ clearAccessibilityFocus();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -10457,6 +11024,8 @@
resetResolvedLayoutDirection();
resetResolvedTextAlignment();
+ resetAccessibilityStateChanged();
+ clearAccessibilityFocus();
}
/**
@@ -13273,6 +13842,29 @@
}
/**
+ * @hide
+ */
+ public Insets getLayoutInsets() {
+ if (mLayoutInsets == null) {
+ if (mBackground == null) {
+ mLayoutInsets = Insets.NONE;
+ } else {
+ Rect insetRect = new Rect();
+ boolean hasInsets = mBackground.getLayoutInsets(insetRect);
+ mLayoutInsets = hasInsets ? Insets.of(insetRect) : Insets.NONE;
+ }
+ }
+ return mLayoutInsets;
+ }
+
+ /**
+ * @hide
+ */
+ public void setLayoutInsets(Insets layoutInsets) {
+ mLayoutInsets = layoutInsets;
+ }
+
+ /**
* Changes the selection state of this view. A view can be selected or not.
* Note that selection is not the same as focus. Views are typically
* selected in the context of an AdapterView like ListView or GridView;
@@ -13287,6 +13879,9 @@
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ notifyAccessibilityStateChanged();
+ }
}
}
@@ -13456,7 +14051,7 @@
position[1] += view.mTop;
viewParent = view.mParent;
- }
+ }
if (viewParent instanceof ViewRootImpl) {
// *cough*
@@ -16291,7 +16886,7 @@
/**
* Temporary list for use in collecting focusable descendents of a view.
*/
- final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
+ final ArrayList<View> mTempArrayList = new ArrayList<View>(24);
/**
* The id of the window for accessibility purposes.
@@ -16299,6 +16894,17 @@
int mAccessibilityWindowId = View.NO_ID;
/**
+ * Whether to ingore not exposed for accessibility Views when
+ * reporting the view tree to accessibility services.
+ */
+ boolean mIncludeNotImportantViews;
+
+ /**
+ * The drawable for highlighting accessibility focus.
+ */
+ Drawable mAccessibilityFocusDrawable;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9d06145..9134966 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -197,7 +197,7 @@
* gesture and the touch up event of a subsequent tap for the latter tap to be
* considered as a tap i.e. to perform a click.
*/
- private static final int TOUCH_EXPLORATION_TAP_SLOP = 80;
+ private static final int TOUCH_EXPLORE_TAP_SLOP = 80;
/**
* Delay before dispatching a recurring accessibility event in milliseconds.
@@ -238,7 +238,7 @@
private final int mDoubleTapTouchSlop;
private final int mPagingTouchSlop;
private final int mDoubleTapSlop;
- private final int mScaledTouchExplorationTapSlop;
+ private final int mScaledTouchExploreTapSlop;
private final int mWindowTouchSlop;
private final int mMaximumDrawingCacheSize;
private final int mOverscrollDistance;
@@ -265,7 +265,7 @@
mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
mPagingTouchSlop = PAGING_TOUCH_SLOP;
mDoubleTapSlop = DOUBLE_TAP_SLOP;
- mScaledTouchExplorationTapSlop = TOUCH_EXPLORATION_TAP_SLOP;
+ mScaledTouchExploreTapSlop = TOUCH_EXPLORE_TAP_SLOP;
mWindowTouchSlop = WINDOW_TOUCH_SLOP;
//noinspection deprecation
mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -302,7 +302,7 @@
mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
- mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f);
+ mScaledTouchExploreTapSlop = (int) (density * TOUCH_EXPLORE_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
final Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -559,8 +559,8 @@
*
* @hide
*/
- public int getScaledTouchExplorationTapSlop() {
- return mScaledTouchExplorationTapSlop;
+ public int getScaledTouchExploreTapSlop() {
+ return mScaledTouchExploreTapSlop;
}
/**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8f6badf..cb37a1c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -141,6 +141,18 @@
public static final String DEBUG_LATENCY_TAG = "ViewLatency";
/**
+ * Enables detailed logging of accessibility focus operations.
+ * @hide
+ */
+ public static final boolean DEBUG_ACCESSIBILITY_FOCUS = false;
+
+ /**
+ * Tag for logging of accessibility focus operations
+ * @hide
+ */
+ public static final String DEBUG_ACCESSIBILITY_FOCUS_TAG = "AccessibilityFocus";
+
+ /**
* <p>Enables or disables views consistency check. Even when this property is enabled,
* view consistency checks happen only if {@link false} is set
* to true. The value of this property can be configured externally in one of the
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 121b544..0b973e5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -45,6 +45,7 @@
import com.android.internal.util.Predicate;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
/**
@@ -169,6 +170,12 @@
*/
protected int mGroupFlags;
+ /*
+ * THe layout mode: either {@link #UNDEFINED_LAYOUT_MODE}, {@link #COMPONENT_BOUNDS} or
+ * {@link #LAYOUT_BOUNDS}
+ */
+ private int mLayoutMode = UNDEFINED_LAYOUT_MODE;
+
/**
* NOTE: If you change the flags below make sure to reflect the changes
* the DisplayList class
@@ -334,6 +341,22 @@
*/
public static final int PERSISTENT_ALL_CACHES = 0x3;
+ // Layout Modes
+
+ private static final int UNDEFINED_LAYOUT_MODE = -1;
+
+ /**
+ * This constant is a {@link #setLayoutMode(int) layoutMode}.
+ * Component bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
+ * {@link #getRight() right} and {@link #getBottom() bottom}.
+ */
+ public static final int COMPONENT_BOUNDS = 0;
+
+ /**
+ * This constant is a {@link #setLayoutMode(int) layoutMode}.
+ */
+ public static final int LAYOUT_BOUNDS = 1;
+
/**
* We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
* are set at the same time.
@@ -611,13 +634,13 @@
/**
* {@inheritDoc}
*/
+ @Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- ViewParent parent = getParent();
+ ViewParent parent = mParent;
if (parent == null) {
return false;
}
final boolean propagate = onRequestSendAccessibilityEvent(child, event);
- //noinspection SimplifiableIfStatement
if (!propagate) {
return false;
}
@@ -1552,6 +1575,33 @@
return mFirstHoverTarget != null;
}
+ @Override
+ public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+ View[] children = mChildren;
+ final int childrenCount = mChildrenCount;
+ for (int i = 0; i < childrenCount; i++) {
+ View child = children[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+ && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+ if (child.includeForAccessibility()) {
+ childrenForAccessibility.add(child);
+ } else {
+ child.addChildrenForAccessibility(childrenForAccessibility);
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void childAccessibilityStateChanged(View child) {
+ if (mParent != null) {
+ mParent.childAccessibilityStateChanged(child);
+ }
+ }
+
/**
* Implement this method to intercept hover events before they are handled
* by child views.
@@ -2294,33 +2344,43 @@
@Override
boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
- if (handled) {
- return handled;
+ boolean handled = false;
+ if (includeForAccessibility()) {
+ handled = super.dispatchPopulateAccessibilityEventInternal(event);
+ if (handled) {
+ return handled;
+ }
}
// Let our children have a shot in populating the event.
- for (int i = 0, count = getChildCount(); i < count; i++) {
- View child = getChildAt(i);
+ ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
+ final int childCount = children.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = children.getChildAt(i);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
- handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+ handled = child.dispatchPopulateAccessibilityEvent(event);
if (handled) {
+ children.recycle();
return handled;
}
}
}
+ children.recycle();
return false;
}
@Override
void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- info.setClassName(ViewGroup.class.getName());
- for (int i = 0, count = mChildrenCount; i < count; i++) {
- View child = mChildren[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
- && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+ if (mAttachInfo != null) {
+ ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
+ childrenForAccessibility.clear();
+ addChildrenForAccessibility(childrenForAccessibility);
+ final int childrenForAccessibilityCount = childrenForAccessibility.size();
+ for (int i = 0; i < childrenForAccessibilityCount; i++) {
+ View child = childrenForAccessibility.get(i);
info.addChild(child);
}
+ childrenForAccessibility.clear();
}
}
@@ -2331,6 +2391,20 @@
}
/**
+ * @hide
+ */
+ @Override
+ public void resetAccessibilityStateChanged() {
+ super.resetAccessibilityStateChanged();
+ View[] children = mChildren;
+ final int childCount = mChildrenCount;
+ for (int i = 0; i < childCount; i++) {
+ View child = children[i];
+ child.resetAccessibilityStateChanged();
+ }
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -3400,6 +3474,10 @@
clearChildFocus(view);
ensureInputFocusOnFirstFocusable();
}
+
+ if (view.isAccessibilityFocused()) {
+ view.clearAccessibilityFocus();
+ }
}
/**
@@ -4368,6 +4446,50 @@
}
/**
+ * Returns the basis of alignment during the layout of this view group:
+ * either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}.
+ *
+ * @return the layout mode to use during layout operations
+ *
+ * @see #setLayoutMode(int)
+ */
+ public int getLayoutMode() {
+ if (mLayoutMode == UNDEFINED_LAYOUT_MODE) {
+ ViewParent parent = getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) parent;
+ return viewGroup.getLayoutMode();
+ } else {
+ int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ boolean preJellyBean = targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
+ return preJellyBean ? COMPONENT_BOUNDS : LAYOUT_BOUNDS;
+ }
+
+ }
+ return mLayoutMode;
+ }
+
+ /**
+ * Sets the basis of alignment during alignment of this view group.
+ * Valid values are either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}.
+ * <p>
+ * The default is to query the property of the parent if this view group has a parent.
+ * If this ViewGroup is the root of the view hierarchy the default
+ * value is {@link #LAYOUT_BOUNDS} for target SDK's greater than JellyBean,
+ * {@link #LAYOUT_BOUNDS} otherwise.
+ *
+ * @param layoutMode the layout mode to use during layout operations
+ *
+ * @see #getLayoutMode()
+ */
+ public void setLayoutMode(int layoutMode) {
+ if (mLayoutMode != layoutMode) {
+ mLayoutMode = layoutMode;
+ requestLayout();
+ }
+ }
+
+ /**
* Returns a new set of layout parameters based on the supplied attributes set.
*
* @param attrs the attributes to build the layout parameters from
@@ -5622,4 +5744,218 @@
}
}
}
+
+ /**
+ * Pooled class that orderes the children of a ViewGroup from start
+ * to end based on how they are laid out and the layout direction.
+ */
+ static class ChildListForAccessibility {
+
+ private static final int MAX_POOL_SIZE = 32;
+
+ private static final Object sPoolLock = new Object();
+
+ private static ChildListForAccessibility sPool;
+
+ private static int sPoolSize;
+
+ private boolean mIsPooled;
+
+ private ChildListForAccessibility mNext;
+
+ private final ArrayList<View> mChildren = new ArrayList<View>();
+
+ private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
+
+ public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
+ ChildListForAccessibility list = null;
+ synchronized (sPoolLock) {
+ if (sPool != null) {
+ list = sPool;
+ sPool = list.mNext;
+ list.mNext = null;
+ list.mIsPooled = false;
+ sPoolSize--;
+ } else {
+ list = new ChildListForAccessibility();
+ }
+ list.init(parent, sort);
+ return list;
+ }
+ }
+
+ public void recycle() {
+ if (mIsPooled) {
+ throw new IllegalStateException("Instance already recycled.");
+ }
+ clear();
+ if (sPoolSize < MAX_POOL_SIZE) {
+ mNext = sPool;
+ mIsPooled = true;
+ sPool = this;
+ sPoolSize++;
+ }
+ }
+
+ public int getChildCount() {
+ return mChildren.size();
+ }
+
+ public View getChildAt(int index) {
+ return mChildren.get(index);
+ }
+
+ public int getChildIndex(View child) {
+ return mChildren.indexOf(child);
+ }
+
+ private void init(ViewGroup parent, boolean sort) {
+ ArrayList<View> children = mChildren;
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = parent.getChildAt(i);
+ children.add(child);
+ }
+ if (sort) {
+ ArrayList<ViewLocationHolder> holders = mHolders;
+ for (int i = 0; i < childCount; i++) {
+ View child = children.get(i);
+ ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
+ holders.add(holder);
+ }
+ Collections.sort(holders);
+ for (int i = 0; i < childCount; i++) {
+ ViewLocationHolder holder = holders.get(i);
+ children.set(i, holder.mView);
+ holder.recycle();
+ }
+ holders.clear();
+ }
+ }
+
+ private void clear() {
+ mChildren.clear();
+ }
+ }
+
+ /**
+ * Pooled class that holds a View and its location with respect to
+ * a specified root. This enables sorting of views based on their
+ * coordinates without recomputing the position relative to the root
+ * on every comparison.
+ */
+ static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
+
+ private static final int MAX_POOL_SIZE = 32;
+
+ private static final Object sPoolLock = new Object();
+
+ private static ViewLocationHolder sPool;
+
+ private static int sPoolSize;
+
+ private boolean mIsPooled;
+
+ private ViewLocationHolder mNext;
+
+ private final Rect mLocation = new Rect();
+
+ public View mView;
+
+ private int mLayoutDirection;
+
+ public static ViewLocationHolder obtain(ViewGroup root, View view) {
+ ViewLocationHolder holder = null;
+ synchronized (sPoolLock) {
+ if (sPool != null) {
+ holder = sPool;
+ sPool = holder.mNext;
+ holder.mNext = null;
+ holder.mIsPooled = false;
+ sPoolSize--;
+ } else {
+ holder = new ViewLocationHolder();
+ }
+ holder.init(root, view);
+ return holder;
+ }
+ }
+
+ public void recycle() {
+ if (mIsPooled) {
+ throw new IllegalStateException("Instance already recycled.");
+ }
+ clear();
+ if (sPoolSize < MAX_POOL_SIZE) {
+ mNext = sPool;
+ mIsPooled = true;
+ sPool = this;
+ sPoolSize++;
+ }
+ }
+
+ @Override
+ public int compareTo(ViewLocationHolder another) {
+ // This instance is greater than an invalid argument.
+ if (another == null) {
+ return 1;
+ }
+ if (getClass() != another.getClass()) {
+ return 1;
+ }
+ // First is above second.
+ if (mLocation.bottom - another.mLocation.top <= 0) {
+ return -1;
+ }
+ // First is below second.
+ if (mLocation.top - another.mLocation.bottom >= 0) {
+ return 1;
+ }
+ // LTR
+ if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
+ final int leftDifference = mLocation.left - another.mLocation.left;
+ // First more to the left than second.
+ if (leftDifference != 0) {
+ return leftDifference;
+ }
+ } else { // RTL
+ final int rightDifference = mLocation.right - another.mLocation.right;
+ // First more to the right than second.
+ if (rightDifference != 0) {
+ return -rightDifference;
+ }
+ }
+ // Break tie by top.
+ final int topDiference = mLocation.top - another.mLocation.top;
+ if (topDiference != 0) {
+ return topDiference;
+ }
+ // Break tie by height.
+ final int heightDiference = mLocation.height() - another.mLocation.height();
+ if (heightDiference != 0) {
+ return -heightDiference;
+ }
+ // Break tie by width.
+ final int widthDiference = mLocation.width() - another.mLocation.width();
+ if (widthDiference != 0) {
+ return -widthDiference;
+ }
+ // Return nondeterministically one of them since we do
+ // not want to ignore any views.
+ return 1;
+ }
+
+ private void init(ViewGroup root, View view) {
+ Rect viewLocation = mLocation;
+ view.getDrawingRect(viewLocation);
+ root.offsetDescendantRectToMyCoords(view, viewLocation);
+ mView = view;
+ mLayoutDirection = root.getResolvedLayoutDirection();
+ }
+
+ private void clear() {
+ mView = null;
+ mLocation.set(0, 0, 0, 0);
+ }
+ }
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 75e9151..ddff91d 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -277,4 +277,22 @@
* View.fitSystemWindows(Rect)} be performed.
*/
public void requestFitSystemWindows();
+
+ /**
+ * Gets the parent of a given View for accessibility. Since some Views are not
+ * exposed to the accessibility layer the parent for accessibility is not
+ * necessarily the direct parent of the View, rather it is a predecessor.
+ *
+ * @return The parent or <code>null</code> if no such is found.
+ */
+ public ViewParent getParentForAccessibility();
+
+ /**
+ * A child notifies its parent that its state for accessibility has changed.
+ * That is some of the child properties reported to accessibility services has
+ * changed, hence the interested services have to be notified for the new state.
+ *
+ * @hide
+ */
+ public void childAccessibilityStateChanged(View child);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2e3ff38..b4554d5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -37,6 +37,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
@@ -56,17 +57,11 @@
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.SparseLongArray;
import android.util.TypedValue;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -79,6 +74,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
+import com.android.internal.R;
import com.android.internal.policy.PolicyManager;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.IInputMethodCallback;
@@ -89,9 +85,7 @@
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.HashSet;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -181,6 +175,10 @@
View mFocusedView;
View mRealFocusedView; // this is not set to null in touch mode
View mOldFocusedView;
+
+ View mAccessibilityFocusedHost;
+ AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
+
int mViewVisibility;
boolean mAppVisible = true;
int mOrigWindowType = -1;
@@ -321,7 +319,7 @@
SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
- AccessibilityNodePrefetcher mAccessibilityNodePrefetcher;
+ HashSet<View> mTempHashSet;
private final int mDensity;
@@ -630,6 +628,10 @@
if (mAccessibilityManager.isEnabled()) {
mAccessibilityInteractionConnectionManager.ensureConnection();
}
+
+ if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
}
}
@@ -1418,6 +1420,8 @@
mView.draw(layerCanvas);
+ drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
+
mResizeBufferStartTime = SystemClock.uptimeMillis();
mResizeBufferDuration = mView.getResources().getInteger(
com.android.internal.R.integer.config_mediumAnimTime);
@@ -1712,7 +1716,7 @@
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
- postSendWindowContentChangedCallback();
+ postSendWindowContentChangedCallback(mView);
}
}
@@ -1880,6 +1884,7 @@
mResizePaint.setAlpha(mResizeAlpha);
canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
}
+ drawAccessibilityFocusedDrawableIfNeeded(canvas);
}
/**
@@ -2234,6 +2239,8 @@
mView.draw(canvas);
+ drawAccessibilityFocusedDrawableIfNeeded(canvas);
+
if (ViewDebug.DEBUG_LATENCY) {
long now = System.nanoTime();
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
@@ -2274,6 +2281,64 @@
return true;
}
+ /**
+ * We want to draw a highlight around the current accessibility focused.
+ * Since adding a style for all possible view is not a viable option we
+ * have this specialized drawing method.
+ *
+ * Note: We are doing this here to be able to draw the highlight for
+ * virtual views in addition to real ones.
+ *
+ * @param canvas The canvas on which to draw.
+ */
+ private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
+ if (!AccessibilityManager.getInstance(mView.mContext).isEnabled()) {
+ return;
+ }
+ if (mAccessibilityFocusedHost == null || mAccessibilityFocusedHost.mAttachInfo == null) {
+ return;
+ }
+ Drawable drawable = getAccessibilityFocusedDrawable();
+ if (drawable == null) {
+ return;
+ }
+ AccessibilityNodeProvider provider =
+ mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+ Rect bounds = mView.mAttachInfo.mTmpInvalRect;
+ if (provider == null) {
+ mAccessibilityFocusedHost.getDrawingRect(bounds);
+ if (mView instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) mView;
+ viewGroup.offsetDescendantRectToMyCoords(mAccessibilityFocusedHost, bounds);
+ }
+ } else {
+ if (mAccessibilityFocusedVirtualView == null) {
+ mAccessibilityFocusedVirtualView = provider.findAccessibilitiyFocus(View.NO_ID);
+ }
+ mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
+ bounds.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
+ }
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
+ }
+
+ private Drawable getAccessibilityFocusedDrawable() {
+ if (mAttachInfo != null) {
+ // Lazily load the accessibility focus drawable.
+ if (mAttachInfo.mAccessibilityFocusDrawable == null) {
+ TypedValue value = new TypedValue();
+ final boolean resolved = mView.mContext.getTheme().resolveAttribute(
+ R.attr.accessibilityFocusedDrawable, value, true);
+ if (resolved) {
+ mAttachInfo.mAccessibilityFocusDrawable =
+ mView.mContext.getResources().getDrawable(value.resourceId);
+ }
+ }
+ return mAttachInfo.mAccessibilityFocusDrawable;
+ }
+ return null;
+ }
+
void invalidateDisplayLists() {
final ArrayList<DisplayList> displayLists = mDisplayLists;
final int count = displayLists.size();
@@ -2407,6 +2472,14 @@
return handled;
}
+ void setAccessibilityFocusedHost(View host) {
+ if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) {
+ mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
+ }
+ mAccessibilityFocusedHost = host;
+ mAccessibilityFocusedVirtualView = null;
+ }
+
public void requestChildFocus(View child, View focused) {
checkThread();
@@ -2437,9 +2510,13 @@
mFocusedView = mRealFocusedView = null;
}
+ @Override
+ public ViewParent getParentForAccessibility() {
+ return null;
+ }
+
public void focusableViewAvailable(View v) {
checkThread();
-
if (mView != null) {
if (!mView.hasFocus()) {
v.requestFocus();
@@ -2547,7 +2624,7 @@
/**
* Return true if child is an ancestor of parent, (or equal to the parent).
*/
- private static boolean isViewDescendantOf(View child, View parent) {
+ static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
@@ -2585,13 +2662,9 @@
private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16;
private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17;
private final static int MSG_UPDATE_CONFIGURATION = 18;
- private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 19;
- private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 20;
- private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
- private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
- private final static int MSG_PROCESS_INPUT_EVENTS = 23;
- private final static int MSG_DISPATCH_SCREEN_STATE = 24;
- private final static int MSG_INVALIDATE_DISPLAY_LIST = 25;
+ private final static int MSG_PROCESS_INPUT_EVENTS = 19;
+ private final static int MSG_DISPATCH_SCREEN_STATE = 20;
+ private final static int MSG_INVALIDATE_DISPLAY_LIST = 21;
final class ViewRootHandler extends Handler {
@Override
@@ -2633,14 +2706,6 @@
return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";
case MSG_UPDATE_CONFIGURATION:
return "MSG_UPDATE_CONFIGURATION";
- case MSG_PERFORM_ACCESSIBILITY_ACTION:
- return "MSG_PERFORM_ACCESSIBILITY_ACTION";
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
- return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
- return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
- return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
case MSG_PROCESS_INPUT_EVENTS:
return "MSG_PROCESS_INPUT_EVENTS";
case MSG_DISPATCH_SCREEN_STATE:
@@ -2770,8 +2835,28 @@
mHasHadWindowFocus = true;
}
- if (hasWindowFocus && mView != null && mAccessibilityManager.isEnabled()) {
- mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ if (mView != null && mAccessibilityManager.isEnabled()) {
+ if (hasWindowFocus) {
+ mView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ // Give accessibility focus to the view that has input
+ // focus if such, otherwise to the first one.
+ if (mView instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) mView;
+ View focused = viewGroup.findFocus();
+ if (focused != null) {
+ focused.requestAccessibilityFocus();
+ }
+ }
+ // There is no accessibility focus, despite our effort
+ // above, now just give it to the first view.
+ if (mAccessibilityFocusedHost == null) {
+ mView.requestAccessibilityFocus();
+ }
+ } else {
+ // Clear accessibility focus when the window loses input focus.
+ setAccessibilityFocusedHost(null);
+ }
}
}
} break;
@@ -2828,30 +2913,6 @@
}
updateConfiguration(config, false);
} break;
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
- if (mView != null) {
- getAccessibilityInteractionController()
- .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
- }
- } break;
- case MSG_PERFORM_ACCESSIBILITY_ACTION: {
- if (mView != null) {
- getAccessibilityInteractionController()
- .perfromAccessibilityActionUiThread(msg);
- }
- } break;
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
- if (mView != null) {
- getAccessibilityInteractionController()
- .findAccessibilityNodeInfoByViewIdUiThread(msg);
- }
- } break;
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
- if (mView != null) {
- getAccessibilityInteractionController()
- .findAccessibilityNodeInfosByTextUiThread(msg);
- }
- } break;
case MSG_DISPATCH_SCREEN_STATE: {
if (mView != null) {
handleScreenStateChange(msg.arg1 == 1);
@@ -2917,28 +2978,25 @@
// set yet.
final View focused = mView.findFocus();
if (focused != null && !focused.isFocusableInTouchMode()) {
-
final ViewGroup ancestorToTakeFocus =
findAncestorToTakeFocusInTouchMode(focused);
if (ancestorToTakeFocus != null) {
// there is an ancestor that wants focus after its descendants that
// is focusable in touch mode.. give it focus
return ancestorToTakeFocus.requestFocus();
- } else {
- // nothing appropriate to have focus in touch mode, clear it out
- mView.unFocus();
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
- mFocusedView = null;
- mOldFocusedView = null;
- return true;
}
}
+ // nothing appropriate to have focus in touch mode, clear it out
+ mView.unFocus();
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
+ mFocusedView = null;
+ mOldFocusedView = null;
+ return true;
}
}
return false;
}
-
/**
* Find an ancestor of focused that wants focus after its descendants and is
* focusable in touch mode.
@@ -2964,25 +3022,45 @@
private boolean leaveTouchMode() {
if (mView != null) {
+ boolean inputFocusValid = false;
if (mView.hasFocus()) {
// i learned the hard way to not trust mFocusedView :)
mFocusedView = mView.findFocus();
if (!(mFocusedView instanceof ViewGroup)) {
// some view has focus, let it keep it
- return false;
- } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+ inputFocusValid = true;
+ } else if (((ViewGroup) mFocusedView).getDescendantFocusability() !=
ViewGroup.FOCUS_AFTER_DESCENDANTS) {
// some view group has focus, and doesn't prefer its children
// over itself for focus, so let them keep it.
- return false;
+ inputFocusValid = true;
}
}
-
- // find the best view to give focus to in this brave new non-touch-mode
- // world
- final View focused = focusSearch(null, View.FOCUS_DOWN);
- if (focused != null) {
- return focused.requestFocus(View.FOCUS_DOWN);
+ // In accessibility mode we always have a view that has the
+ // accessibility focus and input focus follows it, i.e. we
+ // try to give input focus to the accessibility focused view.
+ if (!AccessibilityManager.getInstance(mView.mContext).isEnabled()) {
+ // If the current input focus is not valid, find the best view to give
+ // focus to in this brave new non-touch-mode world.
+ if (!inputFocusValid) {
+ final View focused = focusSearch(null, View.FOCUS_DOWN);
+ if (focused != null) {
+ return focused.requestFocus(View.FOCUS_DOWN);
+ }
+ }
+ } else {
+ // If the current input focus is not valid clear it but do not
+ // give it to another view since the accessibility focus is
+ // leading now and the input one follows.
+ if (!inputFocusValid) {
+ if (mFocusedView != null) {
+ mView.unFocus();
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, null);
+ mFocusedView = null;
+ mOldFocusedView = null;
+ return true;
+ }
+ }
}
}
return false;
@@ -3487,37 +3565,36 @@
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int direction = 0;
switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_LEFT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_RIGHT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_UP;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_DOWN;
- }
- break;
- case KeyEvent.KEYCODE_TAB:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_FORWARD;
- } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- direction = View.FOCUS_BACKWARD;
- }
- break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_LEFT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_RIGHT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_UP;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_DOWN;
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_FORWARD;
+ } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+ direction = View.FOCUS_BACKWARD;
+ }
+ break;
}
-
if (direction != 0) {
- View focused = mView != null ? mView.findFocus() : null;
+ View focused = mView.findFocus();
if (focused != null) {
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
@@ -3532,8 +3609,8 @@
v, mTempRect);
}
if (v.requestFocus(direction, mTempRect)) {
- playSoundEffect(
- SoundEffectConstants.getContantForFocusDirection(direction));
+ playSoundEffect(SoundEffectConstants
+ .getContantForFocusDirection(direction));
finishInputEvent(q, true);
return;
}
@@ -3683,22 +3760,11 @@
+ " called when there is no mView");
}
if (mAccessibilityInteractionController == null) {
- mAccessibilityInteractionController = new AccessibilityInteractionController();
+ mAccessibilityInteractionController = new AccessibilityInteractionController(this);
}
return mAccessibilityInteractionController;
}
- public AccessibilityNodePrefetcher getAccessibilityNodePrefetcher() {
- if (mView == null) {
- throw new IllegalStateException("getAccessibilityNodePrefetcher"
- + " called when there is no mView");
- }
- if (mAccessibilityNodePrefetcher == null) {
- mAccessibilityNodePrefetcher = new AccessibilityNodePrefetcher();
- }
- return mAccessibilityNodePrefetcher;
- }
-
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
@@ -4375,15 +4441,19 @@
* This event is send at most once every
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
*/
- private void postSendWindowContentChangedCallback() {
+ private void postSendWindowContentChangedCallback(View source) {
if (mSendWindowContentChangedAccessibilityEvent == null) {
mSendWindowContentChangedAccessibilityEvent =
new SendWindowContentChangedAccessibilityEvent();
}
- if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
- mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
+ View oldSource = mSendWindowContentChangedAccessibilityEvent.mSource;
+ if (oldSource == null) {
+ mSendWindowContentChangedAccessibilityEvent.mSource = source;
mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+ } else {
+ View newSource = getCommonPredecessor(oldSource, source);
+ mSendWindowContentChangedAccessibilityEvent.mSource = newSource;
}
}
@@ -4419,6 +4489,46 @@
return true;
}
+ @Override
+ public void childAccessibilityStateChanged(View child) {
+ postSendWindowContentChangedCallback(child);
+ }
+
+ private View getCommonPredecessor(View first, View second) {
+ if (mAttachInfo != null) {
+ if (mTempHashSet == null) {
+ mTempHashSet = new HashSet<View>();
+ }
+ HashSet<View> seen = mTempHashSet;
+ seen.clear();
+ View firstCurrent = first;
+ while (firstCurrent != null) {
+ seen.add(firstCurrent);
+ ViewParent firstCurrentParent = firstCurrent.mParent;
+ if (firstCurrentParent instanceof View) {
+ firstCurrent = (View) firstCurrentParent;
+ } else {
+ firstCurrent = null;
+ }
+ }
+ View secondCurrent = second;
+ while (secondCurrent != null) {
+ if (seen.contains(secondCurrent)) {
+ seen.clear();
+ return secondCurrent;
+ }
+ ViewParent secondCurrentParent = secondCurrent.mParent;
+ if (secondCurrentParent instanceof View) {
+ secondCurrent = (View) secondCurrentParent;
+ } else {
+ secondCurrent = null;
+ }
+ }
+ seen.clear();
+ }
+ return null;
+ }
+
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
@@ -4953,6 +5063,7 @@
}
} else {
ensureNoConnection();
+ setAccessibilityFocusedHost(null);
}
}
@@ -4991,14 +5102,15 @@
mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl);
}
+ @Override
public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int prefetchFlags, int interrogatingPid, long interrogatingTid) {
+ int flags, int interrogatingPid, long interrogatingTid) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
- interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
@@ -5009,16 +5121,17 @@
}
}
+ @Override
public void performAccessibilityAction(long accessibilityNodeId, int action,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interogatingPid, long interrogatingTid) {
+ int flags, int interogatingPid, long interrogatingTid) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.performAccessibilityActionClientThread(accessibilityNodeId, action,
- interactionId, callback, interogatingPid, interrogatingTid);
+ interactionId, callback, flags, interogatingPid, interrogatingTid);
} else {
- // We cannot make the call and notify the caller so it does not
+ // We cannot make the call and notify the caller so it does not wait.
try {
callback.setPerformAccessibilityActionResult(false, interactionId);
} catch (RemoteException re) {
@@ -5027,16 +5140,17 @@
}
}
+ @Override
public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid) {
+ int flags, int interrogatingPid, long interrogatingTid) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId,
- interactionId, callback, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
} else {
- // We cannot make the call and notify the caller so it does not
+ // We cannot make the call and notify the caller so it does not wait.
try {
callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
@@ -5045,16 +5159,17 @@
}
}
+ @Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid) {
+ int flags, int interrogatingPid, long interrogatingTid) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
- interactionId, callback, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
} else {
- // We cannot make the call and notify the caller so it does not
+ // We cannot make the call and notify the caller so it does not wait.
try {
callback.setFindAccessibilityNodeInfosResult(null, interactionId);
} catch (RemoteException re) {
@@ -5062,606 +5177,54 @@
}
}
}
- }
- /**
- * Computes whether a view is visible on the screen.
- *
- * @param view The view to check.
- * @return Whether the view is visible on the screen.
- */
- private boolean isDisplayedOnScreen(View view) {
- return (view.mAttachInfo != null
- && view.mAttachInfo.mWindowVisibility == View.VISIBLE
- && view.getVisibility() == View.VISIBLE
- && view.getGlobalVisibleRect(mTempRect));
- }
-
- /**
- * 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 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(
- long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
+ @Override
+ public void findFocus(long accessibilityNodeId, int interactionId, int focusType,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
- Message message = mHandler.obtainMessage();
- message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
- message.arg1 = prefetchFlags;
- SomeArgs args = mPool.acquire();
- args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
- args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
- args.argi3 = interactionId;
- args.arg1 = callback;
- message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == Process.myPid()
- && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
+ viewRootImpl.getAccessibilityInteractionController()
+ .findFocusClientThread(accessibilityNodeId, interactionId, focusType,
+ callback, flags, interrogatingPid, interrogatingTid);
} else {
- mHandler.sendMessage(message);
- }
- }
-
- public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
- final int prefetchFlags = message.arg1;
- SomeArgs args = (SomeArgs) message.obj;
- final int accessibilityViewId = args.argi1;
- final int virtualDescendantId = args.argi2;
- final int interactionId = args.argi3;
- final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) args.arg1;
- mPool.release(args);
- List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
- infos.clear();
- try {
- View target = null;
- if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
- target = ViewRootImpl.this.mView;
- } else {
- target = findViewByAccessibilityId(accessibilityViewId);
- }
- if (target != null && isDisplayedOnScreen(target)) {
- getAccessibilityNodePrefetcher().prefetchAccessibilityNodeInfos(target,
- virtualDescendantId, prefetchFlags, infos);
- }
- } finally {
+ // We cannot make the call and notify the caller so it does not wait.
try {
- callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- infos.clear();
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
- /* ignore - the other side will time out */
+ /* best effort - ignore */
}
}
}
- public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
- int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ @Override
+ public void focusSearch(long accessibilityNodeId, int interactionId, int direction,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
- Message message = mHandler.obtainMessage();
- message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
- message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
- SomeArgs args = mPool.acquire();
- args.argi1 = viewId;
- args.argi2 = interactionId;
- args.arg1 = callback;
- message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == Process.myPid()
- && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
+ viewRootImpl.getAccessibilityInteractionController()
+ .focusSearchClientThread(accessibilityNodeId, interactionId, direction,
+ callback, flags, interrogatingPid, interrogatingTid);
} else {
- mHandler.sendMessage(message);
- }
- }
-
- public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
- final int accessibilityViewId = message.arg1;
- SomeArgs args = (SomeArgs) message.obj;
- final int viewId = args.argi1;
- final int interactionId = args.argi2;
- final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) args.arg1;
- mPool.release(args);
- AccessibilityNodeInfo info = null;
- try {
- View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
- root = findViewByAccessibilityId(accessibilityViewId);
- } else {
- root = ViewRootImpl.this.mView;
- }
- if (root != null) {
- View target = root.findViewById(viewId);
- if (target != null && isDisplayedOnScreen(target)) {
- info = target.createAccessibilityNodeInfo();
- }
- }
- } finally {
+ // We cannot make the call and notify the caller so it does not wait.
try {
- callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
} catch (RemoteException re) {
- /* ignore - the other side will time out */
+ /* best effort - ignore */
}
}
}
-
- public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
- String text, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
- long interrogatingTid) {
- Message message = mHandler.obtainMessage();
- message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
- SomeArgs args = mPool.acquire();
- args.arg1 = text;
- args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
- args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
- args.argi3 = interactionId;
- args.arg2 = callback;
- message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == Process.myPid()
- && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
- }
-
- public void findAccessibilityNodeInfosByTextUiThread(Message message) {
- SomeArgs args = (SomeArgs) message.obj;
- final String text = (String) args.arg1;
- final int accessibilityViewId = args.argi1;
- final int virtualDescendantId = args.argi2;
- final int interactionId = args.argi3;
- final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) args.arg2;
- mPool.release(args);
- List<AccessibilityNodeInfo> infos = null;
- try {
- View target;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
- target = findViewByAccessibilityId(accessibilityViewId);
- } else {
- target = ViewRootImpl.this.mView;
- }
- if (target != null && isDisplayedOnScreen(target)) {
- AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
- if (provider != null) {
- infos = provider.findAccessibilityNodeInfosByText(text,
- virtualDescendantId);
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
- ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
- foundViews.clear();
- target.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
- | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
- | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
- if (!foundViews.isEmpty()) {
- infos = mTempAccessibilityNodeInfoList;
- infos.clear();
- final int viewCount = foundViews.size();
- for (int i = 0; i < viewCount; i++) {
- View foundView = foundViews.get(i);
- if (isDisplayedOnScreen(foundView)) {
- provider = foundView.getAccessibilityNodeProvider();
- if (provider != null) {
- List<AccessibilityNodeInfo> infosFromProvider =
- provider.findAccessibilityNodeInfosByText(text,
- virtualDescendantId);
- if (infosFromProvider != null) {
- infos.addAll(infosFromProvider);
- }
- } else {
- infos.add(foundView.createAccessibilityNodeInfo());
- }
- }
- }
- }
- }
- }
- } finally {
- try {
- callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
- }
- }
-
- public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interogatingPid, long interrogatingTid) {
- Message message = mHandler.obtainMessage();
- message.what = MSG_PERFORM_ACCESSIBILITY_ACTION;
- message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
- message.arg2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
- SomeArgs args = mPool.acquire();
- args.argi1 = action;
- args.argi2 = interactionId;
- args.arg1 = callback;
- message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interogatingPid == Process.myPid()
- && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
- }
-
- public void perfromAccessibilityActionUiThread(Message message) {
- final int accessibilityViewId = message.arg1;
- final int virtualDescendantId = message.arg2;
- SomeArgs args = (SomeArgs) message.obj;
- final int action = args.argi1;
- final int interactionId = args.argi2;
- final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) args.arg1;
- mPool.release(args);
- boolean succeeded = false;
- try {
- View target = findViewByAccessibilityId(accessibilityViewId);
- if (target != null && isDisplayedOnScreen(target)) {
- AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
- if (provider != null) {
- succeeded = provider.performAccessibilityAction(action,
- virtualDescendantId);
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_FOCUS: {
- if (!target.hasFocus()) {
- // Get out of touch mode since accessibility
- // wants to move focus around.
- ensureTouchMode(false);
- succeeded = target.requestFocus();
- }
- } break;
- case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
- if (target.hasFocus()) {
- target.clearFocus();
- succeeded = !target.isFocused();
- }
- } break;
- case AccessibilityNodeInfo.ACTION_SELECT: {
- if (!target.isSelected()) {
- target.setSelected(true);
- succeeded = target.isSelected();
- }
- } break;
- case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
- if (target.isSelected()) {
- target.setSelected(false);
- succeeded = !target.isSelected();
- }
- } break;
- }
- }
- }
- } finally {
- try {
- callback.setPerformAccessibilityActionResult(succeeded, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
- }
- }
-
- private View findViewByAccessibilityId(int accessibilityId) {
- View root = ViewRootImpl.this.mView;
- if (root == null) {
- return null;
- }
- View foundView = root.findViewByAccessibilityId(accessibilityId);
- if (foundView != null && foundView.getVisibility() != View.VISIBLE) {
- return null;
- }
- return foundView;
- }
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
- public volatile boolean mIsPending;
+ public View mSource;
public void run() {
- if (mView != null) {
- mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- mIsPending = false;
- }
- }
- }
-
- /**
- * This class encapsulates a prefetching strategy for the accessibility APIs for
- * querying window content. It is responsible to prefetch a batch of
- * AccessibilityNodeInfos in addition to the one for a requested node.
- */
- class AccessibilityNodePrefetcher {
-
- private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
-
- public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
- List<AccessibilityNodeInfo> outInfos) {
- AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
- if (provider == null) {
- AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
- if (root != null) {
- outInfos.add(root);
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
- prefetchPredecessorsOfRealNode(view, outInfos);
- }
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
- prefetchSiblingsOfRealNode(view, outInfos);
- }
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
- prefetchDescendantsOfRealNode(view, outInfos);
- }
- }
- } else {
- AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
- if (root != null) {
- outInfos.add(root);
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
- prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
- }
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
- prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
- }
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
- prefetchDescendantsOfVirtualNode(root, provider, outInfos);
- }
- }
- }
- }
-
- private void prefetchPredecessorsOfRealNode(View view,
- List<AccessibilityNodeInfo> outInfos) {
- ViewParent parent = view.getParent();
- while (parent instanceof View
- && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- View parentView = (View) parent;
- final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
- parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
- AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
- if (info != null) {
- outInfos.add(info);
- }
- parent = parent.getParent();
- }
- }
-
- private void prefetchSiblingsOfRealNode(View current,
- List<AccessibilityNodeInfo> outInfos) {
- ViewParent parent = current.getParent();
- if (parent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) parent;
- final int childCount = parentGroup.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = parentGroup.getChildAt(i);
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE
- && child.getAccessibilityViewId() != current.getAccessibilityViewId()
- && isDisplayedOnScreen(child)) {
- final long childNodeId = AccessibilityNodeInfo.makeNodeId(
- child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
- AccessibilityNodeInfo info = null;
- AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
- if (provider == null) {
- info = child.createAccessibilityNodeInfo();
- } else {
- info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
- }
- if (info != null) {
- outInfos.add(info);
- }
- }
- }
- }
- }
-
- private void prefetchDescendantsOfRealNode(View root,
- List<AccessibilityNodeInfo> outInfos) {
- if (root instanceof ViewGroup) {
- ViewGroup rootGroup = (ViewGroup) root;
- HashMap<View, AccessibilityNodeInfo> addedChildren =
- new HashMap<View, AccessibilityNodeInfo>();
- final int childCount = rootGroup.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = rootGroup.getChildAt(i);
- if (isDisplayedOnScreen(child)
- && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- final long childNodeId = AccessibilityNodeInfo.makeNodeId(
- child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
- AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
- if (provider == null) {
- AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
- if (info != null) {
- outInfos.add(info);
- addedChildren.put(child, null);
- }
- } else {
- AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
- if (info != null) {
- outInfos.add(info);
- addedChildren.put(child, info);
- }
- }
- }
- }
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
- View addedChild = entry.getKey();
- AccessibilityNodeInfo virtualRoot = entry.getValue();
- if (virtualRoot == null) {
- prefetchDescendantsOfRealNode(addedChild, outInfos);
- } else {
- AccessibilityNodeProvider provider =
- addedChild.getAccessibilityNodeProvider();
- prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
- }
- }
- }
- }
- }
-
- private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
- View providerHost, AccessibilityNodeProvider provider,
- List<AccessibilityNodeInfo> outInfos) {
- long parentNodeId = root.getParentNodeId();
- int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
- while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
- final int virtualDescendantId =
- AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
- || accessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
- virtualDescendantId);
- if (parent != null) {
- outInfos.add(parent);
- }
- parentNodeId = parent.getParentNodeId();
- accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
- parentNodeId);
- } else {
- prefetchPredecessorsOfRealNode(providerHost, outInfos);
- return;
- }
- }
- }
-
- private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
- AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
- final long parentNodeId = current.getParentNodeId();
- final int parentAccessibilityViewId =
- AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
- final int parentVirtualDescendantId =
- AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
- || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent =
- provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
- if (parent != null) {
- SparseLongArray childNodeIds = parent.getChildNodeIds();
- final int childCount = childNodeIds.size();
- for (int i = 0; i < childCount; i++) {
- final long childNodeId = childNodeIds.get(i);
- if (childNodeId != current.getSourceNodeId()
- && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- final int childVirtualDescendantId =
- AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
- AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
- childVirtualDescendantId);
- if (child != null) {
- outInfos.add(child);
- }
- }
- }
- }
- } else {
- prefetchSiblingsOfRealNode(providerHost, outInfos);
- }
- }
-
- private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
- AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
- SparseLongArray childNodeIds = root.getChildNodeIds();
- final int initialOutInfosSize = outInfos.size();
- final int childCount = childNodeIds.size();
- for (int i = 0; i < childCount; i++) {
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- final long childNodeId = childNodeIds.get(i);
- AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
- if (child != null) {
- outInfos.add(child);
- }
- }
- }
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
- final int addedChildCount = outInfos.size() - initialOutInfosSize;
- for (int i = 0; i < addedChildCount; i++) {
- AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
- prefetchDescendantsOfVirtualNode(child, provider, outInfos);
- }
+ if (mSource != null) {
+ mSource.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ mSource.resetAccessibilityStateChanged();
+ mSource = null;
}
}
}
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 9152cc3..110c239 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -263,7 +263,7 @@
| LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
- mVibrator = new Vibrator();
+ mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 0998c80..6cb1578 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -508,7 +508,7 @@
public static final int TYPE_VIEW_SELECTED = 0x00000004;
/**
- * Represents the event of focusing a {@link android.view.View}.
+ * Represents the event of setting input focus of a {@link android.view.View}.
*/
public static final int TYPE_VIEW_FOCUSED = 0x00000008;
@@ -549,7 +549,8 @@
public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
/**
- * Represents the event of changing the content of a window.
+ * Represents the event of changing the content of a window and more
+ * specifically the sub-tree rooted at the event's source.
*/
public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
@@ -569,6 +570,16 @@
public static final int TYPE_ANNOUNCEMENT = 0x00004000;
/**
+ * Represents the event of gaining accessibility focus.
+ */
+ public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
+
+ /**
+ * Represents the event of clearing accessibility focus.
+ */
+ public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
+
+ /**
* Mask for {@link AccessibilityEvent} all types.
*
* @see #TYPE_VIEW_CLICKED
@@ -1018,6 +1029,10 @@
return "TYPE_VIEW_SCROLLED";
case TYPE_ANNOUNCEMENT:
return "TYPE_ANNOUNCEMENT";
+ case TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ return "TYPE_VIEW_ACCESSIBILITY_FOCUSED";
+ case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
+ return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED";
default:
return null;
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index be74b31..35f0d9d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -18,7 +18,9 @@
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
@@ -174,7 +176,7 @@
final int interactionId = mInteractionIdCounter.getAndIncrement();
final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
- Thread.currentThread().getId(), prefetchFlags);
+ prefetchFlags, Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
@@ -293,6 +295,96 @@
}
/**
+ * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
+ * specified focus type. The search is performed in the window whose id is specified
+ * and starts from the node whose accessibility id is specified.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id or virtual descendant id from
+ * where to start the search. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+ * to start from the root.
+ * @param focusType The focus type.
+ * @return The accessibility focused {@link AccessibilityNodeInfo}.
+ */
+ public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
+ long accessibilityNodeId, int focusType) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final float windowScale = connection.findFocus(accessibilityWindowId,
+ accessibilityNodeId, focusType, interactionId, this,
+ Thread.currentThread().getId());
+ // If the scale is zero the call has failed.
+ if (windowScale > 0) {
+ AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+ interactionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ return info;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
+ * The search is performed in the window whose id is specified and starts from the
+ * node whose accessibility id is specified.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id or virtual descendant id from
+ * where to start the search. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+ * to start from the root.
+ * @param direction The direction in which to search for focusable.
+ * @return The accessibility focused {@link AccessibilityNodeInfo}.
+ */
+ public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
+ long accessibilityNodeId, int direction) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final float windowScale = connection.focusSearch(accessibilityWindowId,
+ accessibilityNodeId, direction, interactionId, this,
+ Thread.currentThread().getId());
+ // If the scale is zero the call has failed.
+ if (windowScale > 0) {
+ AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+ interactionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ return info;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
+ }
+ }
+ return null;
+ }
+
+ /**
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
*
* @param connectionId The id of a connection for interacting with the system.
@@ -382,7 +474,12 @@
int interactionId) {
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
- List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null;
+ List<AccessibilityNodeInfo> result = null;
+ if (success) {
+ result = mFindAccessibilityNodeInfosResult;
+ } else {
+ result = Collections.emptyList();
+ }
clearResultLocked();
return result;
}
@@ -395,13 +492,18 @@
int interactionId) {
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
- // If the call is not an IPC, i.e. it is made from the same process, we need to
- // instantiate new result list to avoid passing internal instances to clients.
- final boolean isIpcCall = (queryLocalInterface(getInterfaceDescriptor()) == null);
- if (!isIpcCall) {
- mFindAccessibilityNodeInfosResult = new ArrayList<AccessibilityNodeInfo>(infos);
+ if (infos != null) {
+ // If the call is not an IPC, i.e. it is made from the same process, we need to
+ // instantiate new result list to avoid passing internal instances to clients.
+ final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
+ if (!isIpcCall) {
+ mFindAccessibilityNodeInfosResult =
+ new ArrayList<AccessibilityNodeInfo>(infos);
+ } else {
+ mFindAccessibilityNodeInfosResult = infos;
+ }
} else {
- mFindAccessibilityNodeInfosResult = infos;
+ mFindAccessibilityNodeInfosResult = Collections.emptyList();
}
mInteractionId = interactionId;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e37de6f..77fd12a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -204,6 +204,12 @@
* @param event The event to send.
*
* @throws IllegalStateException if accessibility is not enabled.
+ *
+ * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
+ * events is through calling
+ * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+ * instead of this method to allow predecessors to augment/filter events sent by
+ * their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
if (!mIsEnabled) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f616dca..1071c65 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -74,29 +74,57 @@
public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
/** @hide */
- public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000003;
+ public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
+
+ /** @hide */
+ public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
// Actions.
/**
- * Action that focuses the node.
+ * Action that gives input focus to the node.
*/
- public static final int ACTION_FOCUS = 0x00000001;
+ public static final int ACTION_FOCUS = 0x00000001;
/**
- * Action that unfocuses the node.
+ * Action that clears input focus of the node.
*/
- public static final int ACTION_CLEAR_FOCUS = 0x00000002;
+ public static final int ACTION_CLEAR_FOCUS = 0x00000002;
/**
* Action that selects the node.
*/
- public static final int ACTION_SELECT = 0x00000004;
+ public static final int ACTION_SELECT = 0x00000004;
/**
* Action that unselects the node.
*/
- public static final int ACTION_CLEAR_SELECTION = 0x00000008;
+ public static final int ACTION_CLEAR_SELECTION = 0x00000008;
+
+ /**
+ * Action that gives accessibility focus to the node.
+ */
+ public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000010;
+
+ /**
+ * Action that clears accessibility focus of the node.
+ */
+ public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000020;
+
+ /**
+ * Action that clicks on the node info./AccessibilityNodeInfoCache.java
+ */
+ public static final int ACTION_CLICK = 0x00000040;
+
+ /**
+ * The input focus.
+ */
+ public static final int FOCUS_INPUT = 1;
+
+ /**
+ * The accessibility focus.
+ */
+ public static final int FOCUS_ACCESSIBILITY = 2;
// Boolean attributes.
@@ -120,6 +148,8 @@
private static final int PROPERTY_SCROLLABLE = 0x00000200;
+ private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -248,6 +278,57 @@
(root != null) ? root.getAccessibilityViewId() : UNDEFINED;
mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
+
+ /**
+ * Find the view that has the input focus. The search starts from
+ * the view represented by this node info.
+ *
+ * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
+ * {@link #FOCUS_ACCESSIBILITY}.
+ * @return The node info of the focused view or null.
+ *
+ * @see #FOCUS_INPUT
+ * @see #FOCUS_ACCESSIBILITY
+ */
+ public AccessibilityNodeInfo findFocus(int focus) {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return null;
+ }
+ return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
+ mSourceNodeId, focus);
+ }
+
+ /**
+ * Searches for the nearest view in the specified direction that can take
+ * the input focus.
+ *
+ * @param direction The direction. Can be one of:
+ * {@link View#FOCUS_DOWN},
+ * {@link View#FOCUS_UP},
+ * {@link View#FOCUS_LEFT},
+ * {@link View#FOCUS_RIGHT},
+ * {@link View#FOCUS_FORWARD},
+ * {@link View#FOCUS_BACKWARD},
+ * {@link View#ACCESSIBILITY_FOCUS_IN},
+ * {@link View#ACCESSIBILITY_FOCUS_OUT},
+ * {@link View#ACCESSIBILITY_FOCUS_FORWARD},
+ * {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
+ * {@link View#ACCESSIBILITY_FOCUS_UP},
+ * {@link View#ACCESSIBILITY_FOCUS_RIGHT},
+ * {@link View#ACCESSIBILITY_FOCUS_DOWN},
+ * {@link View#ACCESSIBILITY_FOCUS_LEFT}.
+ *
+ * @return The node info for the view that can take accessibility focus.
+ */
+ public AccessibilityNodeInfo focusSearch(int direction) {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return null;
+ }
+ return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
+ mSourceNodeId, direction);
+ }
/**
* Gets the id of the window from which the info comes from.
@@ -642,6 +723,31 @@
}
/**
+ * Gets whether this node is accessibility focused.
+ *
+ * @return True if the node is accessibility focused.
+ */
+ public boolean isAccessibilityFocused() {
+ return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
+ }
+
+ /**
+ * Sets whether this node is accessibility focused.
+ * <p>
+ * <strong>Note:</strong> 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 accessibility focused.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setAccessibilityFocused(boolean focused) {
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
+ }
+
+ /**
* Gets whether this node is selected.
*
* @return True if the node is selected.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index dfbfc70..d2609bb 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -18,6 +18,7 @@
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.SparseLongArray;
/**
* Simple cache for AccessibilityNodeInfos. The cache is mapping an
@@ -54,20 +55,25 @@
* @param event An event.
*/
public void onAccessibilityEvent(AccessibilityEvent event) {
- final int eventType = event.getEventType();
- switch (eventType) {
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_SCROLLED:
- clear();
- break;
- case AccessibilityEvent.TYPE_VIEW_FOCUSED:
- case AccessibilityEvent.TYPE_VIEW_SELECTED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
- final long accessibilityNodeId = event.getSourceNodeId();
- remove(accessibilityNodeId);
- break;
+ if (ENABLED) {
+ final int eventType = event.getEventType();
+ switch (eventType) {
+ case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+ clear();
+ } break;
+ case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+ case AccessibilityEvent.TYPE_VIEW_SELECTED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+ final long accessibilityNodeId = event.getSourceNodeId();
+ remove(accessibilityNodeId);
+ } break;
+ case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+ final long accessibilityNodeId = event.getSourceNodeId();
+ clearSubTree(accessibilityNodeId);
+ } break;
+ }
}
}
@@ -167,4 +173,23 @@
}
}
}
+
+ /**
+ * Clears a subtree rooted at the node with the given id.
+ *
+ * @param rootNodeId The root id.
+ */
+ private void clearSubTree(long rootNodeId) {
+ AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
+ if (current == null) {
+ return;
+ }
+ mCacheImpl.remove(rootNodeId);
+ SparseLongArray childNodeIds = current.getChildNodeIds();
+ final int childCount = childNodeIds.size();
+ for (int i = 0; i < childCount; i++) {
+ final long childNodeId = childNodeIds.valueAt(i);
+ clearSubTree(childNodeId);
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 5890417..19e35dd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -87,6 +87,7 @@
* @return A populated {@link AccessibilityNodeInfo} for a virtual descendant or the
* host View.
*
+ * @see View#createAccessibilityNodeInfo()
* @see AccessibilityNodeInfo
*/
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
@@ -102,6 +103,7 @@
* @param virtualViewId A client defined virtual view id.
* @return True if the action was performed.
*
+ * @see View#performAccessibilityAction(int)
* @see #createAccessibilityNodeInfo(int)
* @see AccessibilityNodeInfo
*/
@@ -127,4 +129,58 @@
int virtualViewId) {
return null;
}
+
+ /**
+ * Finds the accessibility focused {@link AccessibilityNodeInfo}. The search is
+ * relative to the virtual view, i.e. a descendant of the host View, with the
+ * given <code>virtualViewId</code> or the host View itself
+ * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * <strong>Note:</strong> Normally the system is responsible to transparently find
+ * accessibility focused view starting from a given root but for virtual view
+ * hierarchies it is a responsibility of this provider's implementor to find
+ * the accessibility focused virtual view.
+ *
+ * @param virtualViewId A client defined virtual view id which defined
+ * the root of the tree in which to perform the search.
+ * @return A list of node info.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfo
+ */
+ public AccessibilityNodeInfo findAccessibilitiyFocus(int virtualViewId) {
+ return null;
+ }
+
+ /**
+ * Finds {@link AccessibilityNodeInfo} to take accessibility focus in the given
+ * <code>direction</code>. The search is relative to the virtual view, i.e. a
+ * descendant of the host View, with the given <code>virtualViewId</code> or
+ * the host View itself <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * <strong>Note:</strong> Normally the system is responsible to transparently find
+ * the next view to take accessibility focus but for virtual view hierarchies
+ * it is a responsibility of this provider's implementor to compute the next
+ * focusable.
+ *
+ * @param direction The direction in which to search for a focus candidate.
+ * Values are
+ * {@link View#ACCESSIBILITY_FOCUS_IN},
+ * {@link View#ACCESSIBILITY_FOCUS_OUT},
+ * {@link View#ACCESSIBILITY_FOCUS_FORWARD},
+ * {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
+ * {@link View#ACCESSIBILITY_FOCUS_UP},
+ * {@link View#ACCESSIBILITY_FOCUS_DOWN},
+ * {@link View#ACCESSIBILITY_FOCUS_LEFT},
+ * {@link View#ACCESSIBILITY_FOCUS_RIGHT}.
+ * @param virtualViewId A client defined virtual view id which defined
+ * the root of the tree in which to perform the search.
+ * @return A list of node info.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfo
+ */
+ public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
+ return null;
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index d25b3db..78a7d46 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -62,6 +62,7 @@
private static final int PROPERTY_PASSWORD = 0x00000004;
private static final int PROPERTY_FULL_SCREEN = 0x00000080;
private static final int PROPERTY_SCROLLABLE = 0x00000100;
+ private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
private static final int GET_SOURCE_PREFETCH_FLAGS =
AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
@@ -77,7 +78,7 @@
private boolean mIsInPool;
boolean mSealed;
- int mBooleanProperties;
+ int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
int mCurrentItemIndex = UNDEFINED;
int mItemCount = UNDEFINED;
int mFromIndex = UNDEFINED;
@@ -134,6 +135,8 @@
*/
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
+ final boolean important = (root != null) ? root.isImportantForAccessibility() : true;
+ setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
final int rootViewId = (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
@@ -274,6 +277,23 @@
}
/**
+ * Gets if the source is important for accessibility.
+ *
+ * <strong>Note:</strong> Used only internally to determine whether
+ * to deliver the event to a given accessibility service since some
+ * services may want to regard all views for accessibility while others
+ * may want to regard only the important views for accessibility.
+ *
+ * @return True if the source is important for accessibility,
+ * false otherwise.
+ *
+ * @hide
+ */
+ public boolean isImportantForAccessibility() {
+ return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY);
+ }
+
+ /**
* Gets the number of items that can be visited.
*
* @return The number of items.
@@ -755,7 +775,7 @@
*/
void clear() {
mSealed = false;
- mBooleanProperties = 0;
+ mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
mCurrentItemIndex = UNDEFINED;
mItemCount = UNDEFINED;
mFromIndex = UNDEFINED;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index fc3651c..8182d29 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -28,18 +28,26 @@
oneway interface IAccessibilityInteractionConnection {
void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
- int interrogatingPid, long interrogatingTid);
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+ long interrogatingTid);
void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int id, int interactionId,
- IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid);
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+ long interrogatingTid);
void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+ long interrogatingTid);
+
+ void findFocus(long accessibilityNodeId, int interactionId, int focusType,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+ long interrogatingTid);
+
+ void focusSearch(long accessibilityNodeId, int interactionId, int direction,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid);
void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 320c75d..5b5134a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -19,7 +19,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.accessibilityservice.IEventListener;
+import android.accessibilityservice.IAccessibilityServiceClient;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -49,7 +49,8 @@
void removeAccessibilityInteractionConnection(IWindow windowToken);
- void registerUiTestAutomationService(IEventListener listener, in AccessibilityServiceInfo info);
+ void registerUiTestAutomationService(IAccessibilityServiceClient client,
+ in AccessibilityServiceInfo info);
- void unregisterUiTestAutomationService(IEventListener listener);
+ void unregisterUiTestAutomationService(IAccessibilityServiceClient client);
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 800ebc8..64fbdd5 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -147,6 +147,40 @@
}
}
+ private class JsResultReceiver implements JsResult.ResultReceiver {
+ // This prevents a user from interacting with the result before WebCore is
+ // ready to handle it.
+ private boolean mReady;
+ // Tells us if the user tried to confirm or cancel the result before WebCore
+ // is ready.
+ private boolean mTriedToNotifyBeforeReady;
+
+ public JsPromptResult mJsResult = new JsPromptResult(this);
+
+ final void setReady() {
+ mReady = true;
+ if (mTriedToNotifyBeforeReady) {
+ notifyCallbackProxy();
+ }
+ }
+
+ /* Wake up the WebCore thread. */
+ @Override
+ public void onJsResultComplete(JsResult result) {
+ if (mReady) {
+ notifyCallbackProxy();
+ } else {
+ mTriedToNotifyBeforeReady = true;
+ }
+ }
+
+ private void notifyCallbackProxy() {
+ synchronized (CallbackProxy.this) {
+ CallbackProxy.this.notify();
+ }
+ }
+}
+
/**
* Construct a new CallbackProxy.
*/
@@ -479,18 +513,18 @@
String databaseIdentifier =
(String) map.get("databaseIdentifier");
String url = (String) map.get("url");
- long currentQuota =
- ((Long) map.get("currentQuota")).longValue();
- long totalUsedQuota =
- ((Long) map.get("totalUsedQuota")).longValue();
- long estimatedSize =
- ((Long) map.get("estimatedSize")).longValue();
+ long quota =
+ ((Long) map.get("quota")).longValue();
+ long totalQuota =
+ ((Long) map.get("totalQuota")).longValue();
+ long estimatedDatabaseSize =
+ ((Long) map.get("estimatedDatabaseSize")).longValue();
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
mWebChromeClient.onExceededDatabaseQuota(url,
- databaseIdentifier, currentQuota, estimatedSize,
- totalUsedQuota, quotaUpdater);
+ databaseIdentifier, quota, estimatedDatabaseSize,
+ totalQuota, quotaUpdater);
}
break;
@@ -498,15 +532,15 @@
if (mWebChromeClient != null) {
HashMap<String, Object> map =
(HashMap<String, Object>) msg.obj;
- long spaceNeeded =
- ((Long) map.get("spaceNeeded")).longValue();
- long totalUsedQuota =
- ((Long) map.get("totalUsedQuota")).longValue();
+ long requiredStorage =
+ ((Long) map.get("requiredStorage")).longValue();
+ long quota =
+ ((Long) map.get("quota")).longValue();
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
- mWebChromeClient.onReachedMaxAppCacheSize(spaceNeeded,
- totalUsedQuota, quotaUpdater);
+ mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage,
+ quota, quotaUpdater);
}
break;
@@ -531,14 +565,15 @@
case JS_ALERT:
if (mWebChromeClient != null) {
- final JsResult res = (JsResult) msg.obj;
+ final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+ final JsResult res = receiver.mJsResult;
String message = msg.getData().getString("message");
String url = msg.getData().getString("url");
if (!mWebChromeClient.onJsAlert(mWebView.getWebView(), url, message,
res)) {
if (!canShowAlertDialog()) {
res.cancel();
- res.setReady();
+ receiver.setReady();
break;
}
new AlertDialog.Builder(mContext)
@@ -561,20 +596,21 @@
})
.show();
}
- res.setReady();
+ receiver.setReady();
}
break;
case JS_CONFIRM:
if (mWebChromeClient != null) {
- final JsResult res = (JsResult) msg.obj;
+ final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+ final JsResult res = receiver.mJsResult;
String message = msg.getData().getString("message");
String url = msg.getData().getString("url");
if (!mWebChromeClient.onJsConfirm(mWebView.getWebView(), url, message,
res)) {
if (!canShowAlertDialog()) {
res.cancel();
- res.setReady();
+ receiver.setReady();
break;
}
new AlertDialog.Builder(mContext)
@@ -605,13 +641,14 @@
}
// Tell the JsResult that it is ready for client
// interaction.
- res.setReady();
+ receiver.setReady();
}
break;
case JS_PROMPT:
if (mWebChromeClient != null) {
- final JsPromptResult res = (JsPromptResult) msg.obj;
+ final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+ final JsPromptResult res = receiver.mJsResult;
String message = msg.getData().getString("message");
String defaultVal = msg.getData().getString("default");
String url = msg.getData().getString("url");
@@ -619,7 +656,7 @@
defaultVal, res)) {
if (!canShowAlertDialog()) {
res.cancel();
- res.setReady();
+ receiver.setReady();
break;
}
final LayoutInflater factory = LayoutInflater
@@ -662,20 +699,21 @@
}
// Tell the JsResult that it is ready for client
// interaction.
- res.setReady();
+ receiver.setReady();
}
break;
case JS_UNLOAD:
if (mWebChromeClient != null) {
- final JsResult res = (JsResult) msg.obj;
+ final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+ final JsResult res = receiver.mJsResult;
String message = msg.getData().getString("message");
String url = msg.getData().getString("url");
if (!mWebChromeClient.onJsBeforeUnload(mWebView.getWebView(), url,
message, res)) {
if (!canShowAlertDialog()) {
res.cancel();
- res.setReady();
+ receiver.setReady();
break;
}
final String m = mContext.getString(
@@ -700,19 +738,20 @@
})
.show();
}
- res.setReady();
+ receiver.setReady();
}
break;
case JS_TIMEOUT:
if(mWebChromeClient != null) {
- final JsResult res = (JsResult) msg.obj;
+ final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+ final JsResult res = receiver.mJsResult;
if(mWebChromeClient.onJsTimeout()) {
res.confirm();
} else {
res.cancel();
}
- res.setReady();
+ receiver.setReady();
}
break;
@@ -791,7 +830,8 @@
case OPEN_FILE_CHOOSER:
if (mWebChromeClient != null) {
UploadFileMessageData data = (UploadFileMessageData)msg.obj;
- mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType());
+ mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(),
+ data.getCapture());
}
break;
@@ -1331,7 +1371,7 @@
if (mWebChromeClient == null) {
return;
}
- JsResult result = new JsResult(this, false);
+ JsResultReceiver result = new JsResultReceiver();
Message alert = obtainMessage(JS_ALERT, result);
alert.getData().putString("message", message);
alert.getData().putString("url", url);
@@ -1352,7 +1392,7 @@
if (mWebChromeClient == null) {
return false;
}
- JsResult result = new JsResult(this, false);
+ JsResultReceiver result = new JsResultReceiver();
Message confirm = obtainMessage(JS_CONFIRM, result);
confirm.getData().putString("message", message);
confirm.getData().putString("url", url);
@@ -1365,7 +1405,7 @@
Log.e(LOGTAG, Log.getStackTraceString(e));
}
}
- return result.getResult();
+ return result.mJsResult.getResult();
}
public String onJsPrompt(String url, String message, String defaultValue) {
@@ -1374,7 +1414,7 @@
if (mWebChromeClient == null) {
return null;
}
- JsPromptResult result = new JsPromptResult(this);
+ JsResultReceiver result = new JsResultReceiver();
Message prompt = obtainMessage(JS_PROMPT, result);
prompt.getData().putString("message", message);
prompt.getData().putString("default", defaultValue);
@@ -1388,7 +1428,7 @@
Log.e(LOGTAG, Log.getStackTraceString(e));
}
}
- return result.getStringResult();
+ return result.mJsResult.getStringResult();
}
public boolean onJsBeforeUnload(String url, String message) {
@@ -1397,7 +1437,7 @@
if (mWebChromeClient == null) {
return true;
}
- JsResult result = new JsResult(this, true);
+ JsResultReceiver result = new JsResultReceiver();
Message confirm = obtainMessage(JS_UNLOAD, result);
confirm.getData().putString("message", message);
confirm.getData().putString("url", url);
@@ -1410,7 +1450,7 @@
Log.e(LOGTAG, Log.getStackTraceString(e));
}
}
- return result.getResult();
+ return result.mJsResult.getResult();
}
/**
@@ -1422,19 +1462,21 @@
* @param url The URL that caused the quota overflow.
* @param databaseIdentifier The identifier of the database that the
* transaction that caused the overflow was running on.
- * @param currentQuota The current quota the origin is allowed.
- * @param estimatedSize The estimated size of the database.
- * @param totalUsedQuota is the sum of all origins' quota.
+ * @param quota The current quota the origin is allowed.
+ * @param estimatedDatabaseSize The estimated size of the database.
+ * @param totalQuota is the sum of all origins' quota.
* @param quotaUpdater An instance of a class encapsulating a callback
* to WebViewCore to run when the decision to allow or deny more
* quota has been made.
*/
public void onExceededDatabaseQuota(
- String url, String databaseIdentifier, long currentQuota,
- long estimatedSize, long totalUsedQuota,
+ String url, String databaseIdentifier, long quota,
+ long estimatedDatabaseSize, long totalQuota,
WebStorage.QuotaUpdater quotaUpdater) {
if (mWebChromeClient == null) {
- quotaUpdater.updateQuota(currentQuota);
+ // Native-side logic prevents the quota being updated to a smaller
+ // value.
+ quotaUpdater.updateQuota(quota);
return;
}
@@ -1442,9 +1484,9 @@
HashMap<String, Object> map = new HashMap();
map.put("databaseIdentifier", databaseIdentifier);
map.put("url", url);
- map.put("currentQuota", currentQuota);
- map.put("estimatedSize", estimatedSize);
- map.put("totalUsedQuota", totalUsedQuota);
+ map.put("quota", quota);
+ map.put("estimatedDatabaseSize", estimatedDatabaseSize);
+ map.put("totalQuota", totalQuota);
map.put("quotaUpdater", quotaUpdater);
exceededQuota.obj = map;
sendMessage(exceededQuota);
@@ -1453,24 +1495,26 @@
/**
* Called by WebViewCore to inform the Java side that the appcache has
* exceeded its max size.
- * @param spaceNeeded is the amount of disk space that would be needed
- * in order for the last appcache operation to succeed.
- * @param totalUsedQuota is the sum of all origins' quota.
+ * @param requiredStorage is the amount of storage, in bytes, that would be
+ * needed in order for the last appcache operation to succeed.
+ * @param quota is the current quota (for all origins).
* @param quotaUpdater An instance of a class encapsulating a callback
* to WebViewCore to run when the decision to allow or deny a bigger
* app cache size has been made.
*/
- public void onReachedMaxAppCacheSize(long spaceNeeded,
- long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+ public void onReachedMaxAppCacheSize(long requiredStorage,
+ long quota, WebStorage.QuotaUpdater quotaUpdater) {
if (mWebChromeClient == null) {
- quotaUpdater.updateQuota(0);
+ // Native-side logic prevents the quota being updated to a smaller
+ // value.
+ quotaUpdater.updateQuota(quota);
return;
}
Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
HashMap<String, Object> map = new HashMap();
- map.put("spaceNeeded", spaceNeeded);
- map.put("totalUsedQuota", totalUsedQuota);
+ map.put("requiredStorage", requiredStorage);
+ map.put("quota", quota);
map.put("quotaUpdater", quotaUpdater);
msg.obj = map;
sendMessage(msg);
@@ -1540,7 +1584,7 @@
if (mWebChromeClient == null) {
return true;
}
- JsResult result = new JsResult(this, true);
+ JsResultReceiver result = new JsResultReceiver();
Message timeout = obtainMessage(JS_TIMEOUT, result);
synchronized (this) {
sendMessage(timeout);
@@ -1551,7 +1595,7 @@
Log.e(LOGTAG, Log.getStackTraceString(e));
}
}
- return result.getResult();
+ return result.mJsResult.getResult();
}
public void getVisitedHistory(ValueCallback<String[]> callback) {
@@ -1566,10 +1610,12 @@
private static class UploadFileMessageData {
private UploadFile mCallback;
private String mAcceptType;
+ private String mCapture;
- public UploadFileMessageData(UploadFile uploadFile, String acceptType) {
+ public UploadFileMessageData(UploadFile uploadFile, String acceptType, String capture) {
mCallback = uploadFile;
mAcceptType = acceptType;
+ mCapture = capture;
}
public UploadFile getUploadFile() {
@@ -1579,6 +1625,10 @@
public String getAcceptType() {
return mAcceptType;
}
+
+ public String getCapture() {
+ return mCapture;
+ }
}
private class UploadFile implements ValueCallback<Uri> {
@@ -1597,13 +1647,13 @@
/**
* Called by WebViewCore to open a file chooser.
*/
- /* package */ Uri openFileChooser(String acceptType) {
+ /* package */ Uri openFileChooser(String acceptType, String capture) {
if (mWebChromeClient == null) {
return null;
}
Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
UploadFile uploadFile = new UploadFile();
- UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType);
+ UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType, capture);
myMessage.obj = data;
synchronized (this) {
sendMessage(myMessage);
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 5f7ef41..2997c1a 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -26,7 +26,7 @@
* Manages the cookies used by an application's {@link WebView} instances.
* Cookies are manipulated according to RFC2109.
*/
-public final class CookieManager {
+public class CookieManager {
private static CookieManager sRef;
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
index 93eb082..1441541 100755
--- a/core/java/android/webkit/GeolocationPermissions.java
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -50,7 +50,7 @@
// Within WebKit, Geolocation permissions may be applied either temporarily
// (for the duration of the page) or permanently. This class deals only with
// permanent permissions.
-public final class GeolocationPermissions {
+public class GeolocationPermissions {
/**
* A callback interface used by the host application to set the Geolocation
* permission state for an origin.
@@ -293,6 +293,16 @@
postMessage(Message.obtain(null, CLEAR_ALL));
}
+ /**
+ * This class should not be instantiated directly, applications must only use
+ * {@link #getInstance()} to obtain the instance.
+ * Note this constructor was erroneously public and published in SDK levels prior to 16, but
+ * applications using it would receive a non-functional instance of this class (there was no
+ * way to call createHandler() and createUIHandler(), so it would not work).
+ * @hide
+ */
+ public GeolocationPermissions() {}
+
// Native functions, run on the WebKit thread.
private static native Set nativeGetOrigins();
private static native boolean nativeGetAllowed(String origin);
diff --git a/core/java/android/webkit/JsPromptResult.java b/core/java/android/webkit/JsPromptResult.java
index 9fcd1bc..a1bf124 100644
--- a/core/java/android/webkit/JsPromptResult.java
+++ b/core/java/android/webkit/JsPromptResult.java
@@ -18,11 +18,11 @@
/**
- * Public class for handling javascript prompt requests. A
- * JsDialogHandlerInterface implentation will receive a jsPrompt call with a
- * JsPromptResult parameter. This parameter is used to return a result to
- * WebView. The client can call cancel() to cancel the dialog or confirm() with
- * the user's input to confirm the dialog.
+ * Public class for handling JavaScript prompt requests. The WebChromeClient will receive a
+ * {@link WebChromeClient#onJsPrompt(WebView, String, String, String, JsPromptResult)} call with a
+ * JsPromptResult instance as a parameter. This parameter is used to return the result of this user
+ * dialog prompt back to the WebView instance. The client can call cancel() to cancel the dialog or
+ * confirm() with the user's input to confirm the dialog.
*/
public class JsPromptResult extends JsResult {
// String result of the prompt
@@ -36,17 +36,17 @@
confirm();
}
- /*package*/ JsPromptResult(CallbackProxy proxy) {
- super(proxy, /* unused */ false);
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ public JsPromptResult(ResultReceiver receiver) {
+ super(receiver);
}
- /*package*/ String getStringResult() {
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ public String getStringResult() {
return mStringResult;
}
-
- @Override
- /*package*/ void handleDefault() {
- mStringResult = null;
- super.handleDefault();
- }
}
diff --git a/core/java/android/webkit/JsResult.java b/core/java/android/webkit/JsResult.java
index e61ab21..e4e6851 100644
--- a/core/java/android/webkit/JsResult.java
+++ b/core/java/android/webkit/JsResult.java
@@ -16,23 +16,24 @@
package android.webkit;
-
+/**
+ * An instance of this class is passed as a parameter in various {@link WebChromeClient} action
+ * notifications. The object is used as a handle onto the underlying JavaScript-originated request,
+ * and provides a means for the client to indicate whether this action should proceed.
+ */
public class JsResult {
- // This prevents a user from interacting with the result before WebCore is
- // ready to handle it.
- private boolean mReady;
- // Tells us if the user tried to confirm or cancel the result before WebCore
- // is ready.
- private boolean mTriedToNotifyBeforeReady;
- // This is a basic result of a confirm or prompt dialog.
- protected boolean mResult;
/**
- * This is the caller of the prompt and is the object that is waiting.
- * @hide
+ * Callback interface, implemented by the WebViewProvider implementation to receive
+ * notifications when the JavaScript result represented by a JsResult instance has
+ * @hide Only for use by WebViewProvider implementations
*/
- protected final CallbackProxy mProxy;
- // This is the default value of the result.
- private final boolean mDefaultValue;
+ public interface ResultReceiver {
+ public void onJsResultComplete(JsResult result);
+ }
+ // This is the caller of the prompt and is the object that is waiting.
+ private final ResultReceiver mReceiver;
+ // This is a basic result of a confirm or prompt dialog.
+ private boolean mResult;
/**
* Handle the result if the user cancelled the dialog.
@@ -50,36 +51,22 @@
wakeUp();
}
- /*package*/ JsResult(CallbackProxy proxy, boolean defaultVal) {
- mProxy = proxy;
- mDefaultValue = defaultVal;
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ public JsResult(ResultReceiver receiver) {
+ mReceiver = receiver;
}
- /*package*/ final boolean getResult() {
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ public final boolean getResult() {
return mResult;
}
- /*package*/ final void setReady() {
- mReady = true;
- if (mTriedToNotifyBeforeReady) {
- wakeUp();
- }
- }
-
- /*package*/ void handleDefault() {
- setReady();
- mResult = mDefaultValue;
- wakeUp();
- }
-
- /* Wake up the WebCore thread. */
- protected final void wakeUp() {
- if (mReady) {
- synchronized (mProxy) {
- mProxy.notify();
- }
- } else {
- mTriedToNotifyBeforeReady = true;
- }
+ /* Notify the caller that the JsResult has completed */
+ private final void wakeUp() {
+ mReceiver.onJsResultComplete(this);
}
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index a6ef0ce..4e8790b 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -216,37 +216,54 @@
}
/**
- * Tell the client that the database quota for the origin has been exceeded.
- * @param url The URL that triggered the notification
- * @param databaseIdentifier The identifier of the database that caused the
- * quota overflow.
- * @param currentQuota The current quota for the origin.
- * @param estimatedSize The estimated size of the database.
- * @param totalUsedQuota is the sum of all origins' quota.
- * @param quotaUpdater A callback to inform the WebCore thread that a new
- * quota is available. This callback must always be executed at some
- * point to ensure that the sleeping WebCore thread is woken up.
+ * Tell the client that the quota has been exceeded for the Web SQL Database
+ * API for a particular origin and request a new quota. The client must
+ * respond by invoking the
+ * {@link WebStorage.QuotaUpdater#updateQuota(long) updateQuota(long)}
+ * method of the supplied {@link WebStorage.QuotaUpdater} instance. The
+ * minimum value that can be set for the new quota is the current quota. The
+ * default implementation responds with the current quota, so the quota will
+ * not be increased.
+ * @param url The URL of the page that triggered the notification
+ * @param databaseIdentifier The identifier of the database where the quota
+ * was exceeded.
+ * @param quota The quota for the origin, in bytes
+ * @param estimatedDatabaseSize The estimated size of the offending
+ * database, in bytes
+ * @param totalQuota The total quota for all origins, in bytes
+ * @param quotaUpdater An instance of {@link WebStorage.QuotaUpdater} which
+ * must be used to inform the WebView of the new quota.
*/
+ // Note that the callback must always be executed at some point to ensure
+ // that the sleeping WebCore thread is woken up.
public void onExceededDatabaseQuota(String url, String databaseIdentifier,
- long currentQuota, long estimatedSize, long totalUsedQuota,
- WebStorage.QuotaUpdater quotaUpdater) {
+ long quota, long estimatedDatabaseSize, long totalQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
// This default implementation passes the current quota back to WebCore.
// WebCore will interpret this that new quota was declined.
- quotaUpdater.updateQuota(currentQuota);
+ quotaUpdater.updateQuota(quota);
}
/**
- * Tell the client that the Application Cache has exceeded its max size.
- * @param spaceNeeded is the amount of disk space that would be needed
- * in order for the last appcache operation to succeed.
- * @param totalUsedQuota is the sum of all origins' quota.
- * @param quotaUpdater A callback to inform the WebCore thread that a new
- * app cache size is available. This callback must always be executed at
- * some point to ensure that the sleeping WebCore thread is woken up.
+ * Tell the client that the quota has been reached for the Application Cache
+ * API and request a new quota. The client must respond by invoking the
+ * {@link WebStorage.QuotaUpdater#updateQuota(long) updateQuota(long)}
+ * method of the supplied {@link WebStorage.QuotaUpdater} instance. The
+ * minimum value that can be set for the new quota is the current quota. The
+ * default implementation responds with the current quota, so the quota will
+ * not be increased.
+ * @param requiredStorage The amount of storage required by the Application
+ * Cache operation that triggered this notification,
+ * in bytes.
+ * @param quota The quota, in bytes
+ * @param quotaUpdater An instance of {@link WebStorage.QuotaUpdater} which
+ * must be used to inform the WebView of the new quota.
*/
- public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+ // Note that the callback must always be executed at some point to ensure
+ // that the sleeping WebCore thread is woken up.
+ public void onReachedMaxAppCacheSize(long requiredStorage, long quota,
WebStorage.QuotaUpdater quotaUpdater) {
- quotaUpdater.updateQuota(0);
+ quotaUpdater.updateQuota(quota);
}
/**
@@ -346,9 +363,11 @@
* onReceiveValue must be called to wake up the thread.a
* @param acceptType The value of the 'accept' attribute of the input tag
* associated with this file picker.
+ * @param capture The value of the 'capture' attribute of the input tag
+ * associated with this file picker.
* @hide
*/
- public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
+ public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index 54dfab3..9299b71 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -35,7 +35,7 @@
* WebIconDatabase object is a single instance and all methods operate on that
* single object.
*/
-public final class WebIconDatabase {
+public class WebIconDatabase {
private static final String LOGTAG = "WebIconDatabase";
// Global instance of a WebIconDatabase
private static WebIconDatabase sIconDatabase;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index cddd7ab..105285c 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -170,45 +170,62 @@
}
/**
- * Set whether the WebView supports zoom
+ * Sets whether the WebView should support zooming using its on-screen zoom
+ * controls and gestures. The particular zoom mechanisms that should be used
+ * can be set with {@link #setBuiltInZoomControls}. This setting does not
+ * affect zooming performed using the {@link WebView#zoomIn()} and
+ * {@link WebView#zoomOut()} methods.
+ * @param support Whether the WebView should support zoom.
*/
public void setSupportZoom(boolean support) {
throw new MustOverrideException();
}
/**
- * Returns whether the WebView supports zoom
+ * Returns true if the WebView supports zoom. The default is true.
+ * @return True if the WebView supports zoom.
*/
public boolean supportZoom() {
throw new MustOverrideException();
}
/**
- * Sets whether the zoom mechanism built into WebView is used.
+ * Sets whether the WebView should use its built-in zoom mechanisms, as
+ * opposed to separate zoom controls. The built-in zoom mechanisms comprise
+ * on-screen zoom controls, which are displayed over the WebView's content,
+ * and the use of a pinch gesture to control zooming. Whether or not these
+ * on-screen controls are displayed can be set with {@link #setDisplayZoomControls}.
+ * The separate zoom controls are no longer supported, so it is recommended
+ * that this setting is always enabled.
+ * @param enabled Whether the WebView should use the built-in zoom mechanism.
*/
public void setBuiltInZoomControls(boolean enabled) {
throw new MustOverrideException();
}
/**
- * Returns true if the zoom mechanism built into WebView is being used.
+ * Returns true if the zoom mechanisms built into WebView are being used.
+ * The default is false.
+ * @return True if the zoom mechanisms built into WebView are being used.
*/
public boolean getBuiltInZoomControls() {
throw new MustOverrideException();
}
/**
- * Sets whether the on screen zoom buttons are used.
- * A combination of built in zoom controls enabled
- * and on screen zoom controls disabled allows for pinch to zoom
- * to work without the on screen controls
+ * Sets whether the WebView should display on-screen zoom controls when
+ * using the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}.
+ * @param enabled Whether the WebView should display on-screen zoom controls.
*/
public void setDisplayZoomControls(boolean enabled) {
throw new MustOverrideException();
}
/**
- * Returns true if the on screen zoom buttons are being used.
+ * Returns true if the WebView displays on-screen zoom controls when using
+ * the built-in zoom mechanisms. The default is true.
+ * @return True if the WebView displays on-screen zoom controls when using
+ * the built-in zoom mechanisms.
*/
public boolean getDisplayZoomControls() {
throw new MustOverrideException();
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 2300c2e..041791b 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -25,19 +25,34 @@
import java.util.Set;
/**
- * Functionality for manipulating the webstorage databases.
+ * This class is used to manage the JavaScript storage APIs provided by the
+ * {@link WebView}. It manages the Application Cache API, the Web SQL Database
+ * API and the HTML5 Web Storage API.
+ *
+ * The Web SQL Database API provides storage which is private to a given
+ * origin, where an origin comprises the host, scheme and port of a URI.
+ * Similarly, use of the Application Cache API can be attributed to an origin.
+ * This class provides access to the storage use and quotas for these APIs for
+ * a given origin. Origins are represented using {@link WebStorage.Origin}.
*/
-public final class WebStorage {
+public class WebStorage {
/**
- * Encapsulates a callback function to be executed when a new quota is made
- * available. We primarily want this to allow us to call back the sleeping
- * WebCore thread from outside the WebViewCore class (as the native call
- * is private). It is imperative that this the setDatabaseQuota method is
- * executed once a decision to either allow or deny new quota is made,
- * otherwise the WebCore thread will remain asleep.
+ * Encapsulates a callback function which is used to provide a new quota
+ * for a JavaScript storage API. See
+ * {@link WebChromeClient#onExceededDatabaseQuota} and
+ * {@link WebChromeClient#onReachedMaxAppCacheSize}.
*/
+ // We primarily want this to allow us to call back the sleeping WebCore
+ // thread from outside the WebViewCore class (as the native call is
+ // private). It is imperative that the setDatabaseQuota method is
+ // executed after a decision to either allow or deny new quota is made,
+ // otherwise the WebCore thread will remain asleep.
public interface QuotaUpdater {
+ /**
+ * Provide a new quota, specified in bytes.
+ * @param newQuota The new quota, in bytes
+ */
public void updateQuota(long newQuota);
};
@@ -70,7 +85,9 @@
private Handler mUIHandler = null;
/**
- * Class containing the HTML5 database quota and usage for an origin.
+ * This class encapsulates information about the amount of storage
+ * currently used by an origin for the JavaScript storage APIs.
+ * See {@link WebStorage} for details.
*/
public static class Origin {
private String mOrigin = null;
@@ -93,28 +110,32 @@
}
/**
- * An origin string is created using WebCore::SecurityOrigin::toString().
- * Note that WebCore::SecurityOrigin uses 0 (which is not printed) for
- * the port if the port is the default for the protocol. Eg
- * http://www.google.com and http://www.google.com:80 both record a port
- * of 0 and hence toString() == 'http://www.google.com' for both.
- * @return The origin string.
+ * Get the string representation of this origin.
+ * @return The string representation of this origin
*/
+ // An origin string is created using WebCore::SecurityOrigin::toString().
+ // Note that WebCore::SecurityOrigin uses 0 (which is not printed) for
+ // the port if the port is the default for the protocol. Eg
+ // http://www.google.com and http://www.google.com:80 both record a port
+ // of 0 and hence toString() == 'http://www.google.com' for both.
public String getOrigin() {
return mOrigin;
}
/**
- * Returns the quota for this origin's HTML5 database.
- * @return The quota in bytes.
+ * Get the quota for this origin, for the Web SQL Database API, in
+ * bytes. If this origin does not use the Web SQL Database API, this
+ * quota will be set to zero.
+ * @return The quota, in bytes.
*/
public long getQuota() {
return mQuota;
}
/**
- * Returns the usage for this origin's HTML5 database.
- * @return The usage in bytes.
+ * Get the total amount of storage currently being used by this origin,
+ * for all JavaScript storage APIs, in bytes.
+ * @return The total amount of storage, in bytes.
*/
public long getUsage() {
return mUsage;
@@ -122,8 +143,8 @@
}
/**
- * @hide
* Message handler, UI side
+ * @hide
*/
public void createUIHandler() {
if (mUIHandler == null) {
@@ -156,8 +177,8 @@
}
/**
+ * Message handler, WebCore side
* @hide
- * Message handler, webcore side
*/
public synchronized void createHandler() {
if (mHandler == null) {
@@ -231,19 +252,22 @@
/*
* When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(),
- * we need to get the values from webcore, but we cannot block while doing so
- * as we used to do, as this could result in a full deadlock (other webcore
+ * we need to get the values from WebCore, but we cannot block while doing so
+ * as we used to do, as this could result in a full deadlock (other WebCore
* messages received while we are still blocked here, see http://b/2127737).
*
* We have to do everything asynchronously, by providing a callback function.
- * We post a message on the webcore thread (mHandler) that will get the result
- * from webcore, and we post it back on the UI thread (using mUIHandler).
+ * We post a message on the WebCore thread (mHandler) that will get the result
+ * from WebCore, and we post it back on the UI thread (using mUIHandler).
* We can then use the callback function to return the value.
*/
/**
- * Returns a list of origins having a database. The Map is of type
- * Map<String, Origin>.
+ * Get the origins currently using either the Application Cache or Web SQL
+ * Database APIs. This method operates asynchronously, with the result
+ * being provided via a {@link ValueCallback}. The origins are provided as
+ * a map, of type {@code Map<String, WebStorage.Origin>}, from the string
+ * representation of the origin to a {@link WebStorage.Origin} object.
*/
public void getOrigins(ValueCallback<Map> callback) {
if (callback != null) {
@@ -269,7 +293,11 @@
}
/**
- * Returns the use for a given origin
+ * Get the amount of storage currently being used by both the Application
+ * Cache and Web SQL Database APIs by the given origin. The amount is given
+ * in bytes and the origin is specified using its string representation.
+ * This method operates asynchronously, with the result being provided via
+ * a {@link ValueCallback}.
*/
public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
if (callback == null) {
@@ -292,7 +320,11 @@
}
/**
- * Returns the quota for a given origin
+ * Get the storage quota for the Web SQL Database API for the given origin.
+ * The quota is given in bytes and the origin is specified using its string
+ * representation. This method operates asynchronously, with the result
+ * being provided via a {@link ValueCallback}. Note that a quota is not
+ * enforced on a per-origin basis for the Application Cache API.
*/
public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
if (callback == null) {
@@ -315,7 +347,10 @@
}
/**
- * Set the quota for a given origin
+ * Set the storage quota for the Web SQL Database API for the given origin.
+ * The quota is specified in bytes and the origin is specified using its string
+ * representation. Note that a quota is not enforced on a per-origin basis
+ * for the Application Cache API.
*/
public void setQuotaForOrigin(String origin, long quota) {
if (origin != null) {
@@ -329,7 +364,9 @@
}
/**
- * Delete a given origin
+ * Clear the storage currently being used by both the Application Cache and
+ * Web SQL Database APIs by the given origin. The origin is specified using
+ * its string representation.
*/
public void deleteOrigin(String origin) {
if (origin != null) {
@@ -343,7 +380,9 @@
}
/**
- * Delete all databases
+ * Clear all storage currently being used by the JavaScript storage APIs.
+ * This includes the Application Cache, Web SQL Database and the HTML5 Web
+ * Storage APIs.
*/
public void deleteAllData() {
if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
@@ -381,8 +420,8 @@
}
/**
- * Get the global instance of WebStorage.
- * @return A single instance of WebStorage.
+ * Get the singleton instance of this class.
+ * @return The singleton {@link WebStorage} instance.
*/
public static WebStorage getInstance() {
if (sWebStorage == null) {
@@ -404,7 +443,7 @@
}
/**
- * Run on the webcore thread
+ * Run on the WebCore thread
* set the local values with the current ones
*/
private void syncValues() {
@@ -418,6 +457,16 @@
}
}
+ /**
+ * This class should not be instantiated directly, applications must only use
+ * {@link #getInstance()} to obtain the instance.
+ * Note this constructor was erroneously public and published in SDK levels prior to 16, but
+ * applications using it would receive a non-functional instance of this class (there was no
+ * way to call createHandler() and createUIHandler(), so it would not work).
+ * @hide
+ */
+ public WebStorage() {}
+
// Native functions
private static native Set nativeGetOrigins();
private static native long nativeGetUsageForOrigin(String origin);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d1cfc6b..5498622 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1281,8 +1281,10 @@
* @deprecated {@link #findAllAsync} is preferred.
* @see #setFindListener
*/
+ @Deprecated
public int findAll(String find) {
checkThread();
+ StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
return mProvider.findAll(find);
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 851fd22..893849b9 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -4677,6 +4677,9 @@
* Select the word at the indicated content coordinates.
*/
boolean selectText(int x, int y) {
+ if (mWebViewCore == null) {
+ return false;
+ }
mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
return true;
}
@@ -7144,7 +7147,8 @@
if (mFindIsUp) return false;
boolean result = false;
result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
- if (mWebViewCore.getSettings().getNeedInitialFocus() && !mWebView.isInTouchMode()) {
+ if (mWebViewCore.getSettings().getNeedInitialFocus()
+ && !mWebView.isInTouchMode()) {
// For cases such as GMail, where we gain focus from a direction,
// we want to move to the first available link.
// FIXME: If there are no visible links, we may not want to
@@ -7165,7 +7169,7 @@
default:
return result;
}
- // TODO: Send initial focus request to webkit (b/6108927)
+ mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection);
}
return result;
}
@@ -8089,10 +8093,6 @@
mWebView.invalidate();
}
- if (mPictureListener != null) {
- mPictureListener.onNewPicture(getWebView(), capturePicture());
- }
-
// update the zoom information based on the new picture
mZoomManager.onNewPicture(draw);
@@ -8100,6 +8100,10 @@
mViewManager.postReadyToDrawAll();
}
scrollEditWithCursor();
+
+ if (mPictureListener != null) {
+ mPictureListener.onNewPicture(getWebView(), capturePicture());
+ }
}
/**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 15a2d48..e2880d6 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -394,10 +394,12 @@
* Called by JNI. Open a file chooser to upload a file.
* @param acceptType The value of the 'accept' attribute of the
* input tag associated with this file picker.
+ * @param capture The value of the 'capture' attribute of the
+ * input tag associated with this file picker.
* @return String version of the URI.
*/
- private String openFileChooser(String acceptType) {
- Uri uri = mCallbackProxy.openFileChooser(acceptType);
+ private String openFileChooser(String acceptType, String capture) {
+ Uri uri = mCallbackProxy.openFileChooser(acceptType, capture);
if (uri != null) {
String filePath = "";
// Note - querying for MediaStore.Images.Media.DATA
@@ -428,38 +430,38 @@
* Notify the browser that the origin has exceeded it's database quota.
* @param url The URL that caused the overflow.
* @param databaseIdentifier The identifier of the database.
- * @param currentQuota The current quota for the origin.
- * @param estimatedSize The estimated size of the database.
+ * @param quota The current quota for the origin.
+ * @param estimatedDatabaseSize The estimated size of the database.
*/
protected void exceededDatabaseQuota(String url,
String databaseIdentifier,
- long currentQuota,
- long estimatedSize) {
+ long quota,
+ long estimatedDatabaseSize) {
// Inform the callback proxy of the quota overflow. Send an object
// that encapsulates a call to the nativeSetDatabaseQuota method to
// awaken the sleeping webcore thread when a decision from the
// client to allow or deny quota is available.
mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
- currentQuota, estimatedSize, getUsedQuota(),
+ quota, estimatedDatabaseSize, getUsedQuota(),
new WebStorage.QuotaUpdater() {
@Override
- public void updateQuota(long quota) {
- nativeSetNewStorageLimit(mNativeClass, quota);
+ public void updateQuota(long newQuota) {
+ nativeSetNewStorageLimit(mNativeClass, newQuota);
}
});
}
/**
* Notify the browser that the appcache has exceeded its max size.
- * @param spaceNeeded is the amount of disk space that would be needed
- * in order for the last appcache operation to succeed.
+ * @param requiredStorage is the amount of storage, in bytes, that would be
+ * needed in order for the last appcache operation to succeed.
*/
- protected void reachedMaxAppCacheSize(long spaceNeeded) {
- mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
+ protected void reachedMaxAppCacheSize(long requiredStorage) {
+ mCallbackProxy.onReachedMaxAppCacheSize(requiredStorage, getUsedQuota(),
new WebStorage.QuotaUpdater() {
@Override
- public void updateQuota(long quota) {
- nativeSetNewStorageLimit(mNativeClass, quota);
+ public void updateQuota(long newQuota) {
+ nativeSetNewStorageLimit(mNativeClass, newQuota);
}
});
}
@@ -1174,6 +1176,7 @@
// key was pressed (down and up)
static final int KEY_PRESS = 223;
+ static final int SET_INITIAL_FOCUS = 224;
// Private handler for WebCore messages.
private Handler mHandler;
@@ -1746,6 +1749,9 @@
WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget();
break;
}
+ case SET_INITIAL_FOCUS:
+ nativeSetInitialFocus(mNativeClass, msg.arg1);
+ break;
}
}
};
@@ -3069,6 +3075,7 @@
private native void nativeClearTextSelection(int nativeClass);
private native boolean nativeSelectWordAt(int nativeClass, int x, int y);
private native void nativeSelectAll(int nativeClass);
+ private native void nativeSetInitialFocus(int nativeClass, int keyDirection);
private static native void nativeCertTrustChanged();
}
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index c8677ec..0a0afaa 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -269,9 +269,8 @@
*/
public boolean postPointerEvent(MotionEvent event,
int webKitXOffset, int webKitYOffset, float webKitScale) {
- if (event == null
- || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
- throw new IllegalArgumentException("event must be a pointer event");
+ if (event == null) {
+ throw new IllegalArgumentException("event cannot be null");
}
if (DEBUG) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 057aabe..e68049c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2062,6 +2062,10 @@
child = mAdapter.getView(position, scrapView, this);
+ if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
position, getChildCount());
@@ -2082,6 +2086,11 @@
}
} else {
child = mAdapter.getView(position, null, this);
+
+ if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
if (mCacheColorHint != 0) {
child.setDrawingCacheBackgroundColor(mCacheColorHint);
}
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index efdfae3..f279f8e 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -191,6 +191,10 @@
if (view == null) {
// Make a new one
view = mAdapter.getView(selectedPosition, null, this);
+
+ if (view.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
if (view != null) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 97a864c..abfc577 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -24,14 +24,15 @@
import android.util.SparseArray;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
-
/**
* An AdapterView is a view whose children are determined by an {@link Adapter}.
*
@@ -232,6 +233,11 @@
public AdapterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
/**
@@ -643,6 +649,12 @@
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
+ // If not explicitly specified this view is important for accessibility.
+ if (emptyView != null
+ && emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
final T adapter = getAdapter();
final boolean empty = ((adapter == null) || adapter.isEmpty());
updateEmptyStatus(empty);
@@ -846,12 +858,14 @@
}
} else {
fireOnSelected();
+ performAccessibilityActionsOnSelected();
}
}
}
void selectionChanged() {
- if (mOnItemSelectedListener != null) {
+ if (mOnItemSelectedListener != null
+ || AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mInLayout || mBlockLayoutRequests) {
// If we are in a layout traversal, defer notification
// by posting. This ensures that the view tree is
@@ -863,20 +877,16 @@
post(mSelectionNotifier);
} else {
fireOnSelected();
+ performAccessibilityActionsOnSelected();
}
}
-
- // we fire selection events here not in View
- if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- }
}
private void fireOnSelected() {
- if (mOnItemSelectedListener == null)
+ if (mOnItemSelectedListener == null) {
return;
-
- int selection = this.getSelectedItemPosition();
+ }
+ final int selection = getSelectedItemPosition();
if (selection >= 0) {
View v = getSelectedView();
mOnItemSelectedListener.onItemSelected(this, v, selection,
@@ -886,6 +896,17 @@
}
}
+ private void performAccessibilityActionsOnSelected() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+ final int position = getSelectedItemPosition();
+ if (position >= 0) {
+ // we fire selection events here not in View
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ }
+ }
+
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
View selectedView = getSelectedView();
@@ -936,6 +957,24 @@
event.setItemCount(getCount());
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
+ // We prefer to five focus to the child instead of this view.
+ // Usually the children are not actionable for accessibility,
+ // and they will not take accessibility focus, so we give it.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (isTransformedTouchPointInView(x, y, child, null)) {
+ return child.requestAccessibilityFocus();
+ }
+ }
+ return super.onRequestAccessibilityFocusFromHover(x, y);
+ }
+
private boolean isScrollableForAccessibility() {
T adapter = getAdapter();
if (adapter != null) {
@@ -1012,6 +1051,9 @@
mNeedSync = false;
checkSelectionChanged();
}
+
+ //TODO: Hmm, we do not know the old state so this is sub-optimal
+ notifyAccessibilityStateChanged();
}
void checkSelectionChanged() {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index bb00049..c557963 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -414,6 +414,10 @@
// get the fresh child from the adapter
final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+ if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
if (mViewsMap.containsKey(index)) {
final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
// add the new child to the frame, if it exists
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index c5066b6..108b720 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -279,8 +279,13 @@
// re-order the number spinners to match the current date format
reorderSpinners();
- // set content descriptions
+ // accessibility
setContentDescriptions();
+
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cbff58c..040a385 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1681,7 +1681,7 @@
final int itemCount = clipData.getItemCount();
for (int i=0; i < itemCount; i++) {
Item item = clipData.getItemAt(i);
- content.append(item.coerceToText(mTextView.getContext()));
+ content.append(item.coerceToStyledText(mTextView.getContext()));
}
final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 60dd55c..1cb676f5 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
@@ -559,9 +560,9 @@
int flags = (gravity & mask) >> shift;
switch (flags) {
case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
- return LEADING;
+ return horizontal ? LEFT : TOP;
case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
- return TRAILING;
+ return horizontal ? RIGHT : BOTTOM;
case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
return FILL;
case AXIS_SPECIFIED:
@@ -1042,12 +1043,15 @@
int rightMargin = getMargin(c, true, false);
int bottomMargin = getMargin(c, false, false);
- // Alignment offsets: the location of the view relative to its alignment group.
- int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin);
- int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin);
+ int sumMarginsX = leftMargin + rightMargin;
+ int sumMarginsY = topMargin + bottomMargin;
- int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin);
- int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin);
+ // Alignment offsets: the location of the view relative to its alignment group.
+ int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
+ int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
+
+ int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
+ int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
int dx = x1 + gravityOffsetX + alignmentOffsetX;
@@ -1181,7 +1185,7 @@
View c = getChildAt(i);
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
- groupBounds.getValue(i).include(c, spec, GridLayout.this, this);
+ groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
}
}
@@ -2138,16 +2142,30 @@
return before + after;
}
- protected int getOffset(View c, Alignment alignment, int size) {
- return before - alignment.getAlignmentValue(c, size);
+ private int getAlignmentValue(GridLayout gl, View c, int size, Alignment a, boolean horiz) {
+ boolean useLayoutBounds = gl.getLayoutMode() == LAYOUT_BOUNDS;
+ if (!useLayoutBounds) {
+ return a.getAlignmentValue(c, size);
+ } else {
+ Insets insets = c.getLayoutInsets();
+ int leadingInset = horiz ? insets.left : insets.top; // RTL?
+ int trailingInset = horiz ? insets.right : insets.bottom; // RTL?
+ int totalInset = leadingInset + trailingInset;
+ return leadingInset + a.getAlignmentValue(c, size - totalInset);
+ }
}
- protected final void include(View c, Spec spec, GridLayout gridLayout, Axis axis) {
+ protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
+ return before - getAlignmentValue(gl, c, size, a, horizontal);
+ }
+
+ protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
this.flexibility &= spec.getFlexibility();
- int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal);
- Alignment alignment = gridLayout.getAlignment(spec.alignment, axis.horizontal);
+ boolean horizontal = axis.horizontal;
+ int size = gl.getMeasurementIncludingMargin(c, horizontal);
+ Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
// todo test this works correctly when the returned value is UNDEFINED
- int before = alignment.getAlignmentValue(c, size);
+ int before = getAlignmentValue(gl, c, size, alignment, horizontal);
include(before, size - before);
}
@@ -2614,8 +2632,8 @@
}
@Override
- protected int getOffset(View c, Alignment alignment, int size) {
- return max(0, super.getOffset(c, alignment, size));
+ protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
+ return max(0, super.getOffset(gl, c, a, size, hrz));
}
};
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 91e2e497..6c7ea67 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -105,11 +105,11 @@
super(context);
initImageView();
}
-
+
public ImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
-
+
public ImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initImageView();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b2321d9..992849d 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -112,8 +112,7 @@
private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
/**
- * The duration of scrolling to the next/previous value while snapping to
- * a given position.
+ * The duration of scrolling while snapping to a given position.
*/
private static final int SNAP_SCROLL_DURATION = 300;
@@ -680,6 +679,11 @@
mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
updateInputTextView();
+
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
@Override
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 22e9ef1..080b87d 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -80,16 +80,22 @@
/**
* Called when a share target has been selected. The client can
- * decide whether to handle the intent or rely on the default
- * behavior which is launching it.
+ * decide whether to perform some action before the sharing is
+ * actually performed.
* <p>
* <strong>Note:</strong> Modifying the intent is not permitted and
* any changes to the latter will be ignored.
* </p>
+ * <p>
+ * <strong>Note:</strong> You should <strong>not</strong> handle the
+ * intent here. This callback aims to notify the client that a
+ * sharing is being performed, so the client can update the UI
+ * if necessary.
+ * </p>
*
* @param source The source of the notification.
* @param intent The intent for launching the chosen share target.
- * @return Whether the client has handled the intent.
+ * @return The return result is ignored. Always return false for consistency.
*/
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent);
}
@@ -308,7 +314,7 @@
@Override
public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
if (mOnShareTargetSelectedListener != null) {
- return mOnShareTargetSelectedListener.onShareTargetSelected(
+ mOnShareTargetSelectedListener.onShareTargetSelected(
ShareActionProvider.this, intent);
}
return false;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9867e47..37d9db7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1105,6 +1105,11 @@
setLongClickable(longClickable);
if (mEditor != null) mEditor.prepareCursorControllers();
+
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -7710,7 +7715,7 @@
if (clip != null) {
boolean didFirst = false;
for (int i=0; i<clip.getItemCount(); i++) {
- CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
+ CharSequence paste = clip.getItemAt(i).coerceToStyledText(getContext());
if (paste != null) {
if (!didFirst) {
long minMax = prepareSpacesAroundPaste(min, max, paste);
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index bc88b62..18f7a91 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -251,6 +251,11 @@
// set the content descriptions
setContentDescriptions();
+
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
@Override
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index a0e125a..37567fd 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -29,9 +29,9 @@
import android.widget.Toast;
public class PlatLogoActivity extends Activity {
+ Vibrator mZzz;
Toast mToast;
ImageView mContent;
- Vibrator mZzz = new Vibrator();
int mCount;
final Handler mHandler = new Handler();
@@ -63,7 +63,8 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
+ mZzz = (Vibrator)getSystemService(VIBRATOR_SERVICE);
mToast = Toast.makeText(this, "Android 4.0: Ice Cream Sandwich", Toast.LENGTH_SHORT);
mContent = new ImageView(this);
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index 77d0c97..6a46929 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -37,6 +37,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Vibrator;
+import android.os.SystemVibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
@@ -399,7 +400,7 @@
}
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
- Vibrator vibrator = new Vibrator();
+ Vibrator vibrator = new SystemVibrator();
try {
vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
} catch (Exception e) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index d1aa1ce..dbf6c8e 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -201,4 +201,40 @@
}
return array;
}
+
+ public static int[] appendInt(int[] cur, int val) {
+ if (cur == null) {
+ return new int[] { val };
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ return cur;
+ }
+ }
+ int[] ret = new int[N + 1];
+ System.arraycopy(cur, 0, ret, 0, N);
+ ret[N] = val;
+ return ret;
+ }
+
+ public static int[] removeInt(int[] cur, int val) {
+ if (cur == null) {
+ return null;
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ int[] ret = new int[N - 1];
+ if (i > 0) {
+ System.arraycopy(cur, 0, ret, 0, i);
+ }
+ if (i < (N - 1)) {
+ System.arraycopy(cur, i + 1, ret, i, N - i - 1);
+ }
+ return ret;
+ }
+ }
+ return cur;
+ }
}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 3973344..0c5d5ef 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -150,6 +150,24 @@
*/
public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
+ private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED";
+ sCmdToString[CMD_CHANNEL_FULL_CONNECTION - BASE] = "CMD_CHANNEL_FULL_CONNECTION";
+ sCmdToString[CMD_CHANNEL_FULLY_CONNECTED - BASE] = "CMD_CHANNEL_FULLY_CONNECTED";
+ sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT";
+ sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED";
+ }
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return null;
+ }
+ }
+
/** Successful status always 0, !0 is an unsuccessful status */
public static final int STATUS_SUCCESSFUL = 0;
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index 3dd2284..699e9b3 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -46,6 +46,10 @@
mCurrent = mBuilder.toString();
}
+ public void printPair(String key, Object value) {
+ print(key + "=" + String.valueOf(value) + " ");
+ }
+
@Override
public void println() {
super.println();
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 07496a7..1391ac3 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -20,9 +20,13 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.text.TextUtils;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.Vector;
@@ -444,9 +448,11 @@
* The information maintained for a processed message.
*/
public static class ProcessedMessageInfo {
- private int what;
- private State state;
- private State orgState;
+ private long mTime;
+ private int mWhat;
+ private String mInfo;
+ private State mState;
+ private State mOrgState;
/**
* Constructor
@@ -455,8 +461,8 @@
* @param orgState is the first state the received the message but
* did not processes the message.
*/
- ProcessedMessageInfo(Message message, State state, State orgState) {
- update(message, state, orgState);
+ ProcessedMessageInfo(Message msg, String info, State state, State orgState) {
+ update(msg, info, state, orgState);
}
/**
@@ -465,31 +471,47 @@
* @param orgState is the first state the received the message but
* did not processes the message.
*/
- public void update(Message message, State state, State orgState) {
- this.what = message.what;
- this.state = state;
- this.orgState = orgState;
+ public void update(Message msg, String info, State state, State orgState) {
+ mTime = System.currentTimeMillis();
+ mWhat = msg.what;
+ mInfo = info;
+ mState = state;
+ mOrgState = orgState;
+ }
+
+ /**
+ * @return time stamp
+ */
+ public long getTime() {
+ return mTime;
+ }
+
+ /**
+ * @return msg.what
+ */
+ public long getWhat() {
+ return mWhat;
}
/**
* @return the command that was executing
*/
- public int getWhat() {
- return what;
+ public String getInfo() {
+ return mInfo;
}
/**
* @return the state that handled this message
*/
public State getState() {
- return state;
+ return mState;
}
/**
* @return the original state that received the message.
*/
public State getOriginalState() {
- return orgState;
+ return mOrgState;
}
/**
@@ -498,26 +520,24 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("what=");
- sb.append(what);
+ sb.append("time=");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(mTime);
+ sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
sb.append(" state=");
- sb.append(cn(state));
+ sb.append(mState == null ? "<null>" : mState.getName());
sb.append(" orgState=");
- sb.append(cn(orgState));
- return sb.toString();
- }
-
- /**
- * @return an objects class name
- */
- private String cn(Object n) {
- if (n == null) {
- return "null";
- } else {
- String name = n.getClass().getName();
- int lastDollar = name.lastIndexOf('$');
- return name.substring(lastDollar + 1);
+ sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
+ sb.append(" what=");
+ sb.append(mWhat);
+ sb.append("(0x");
+ sb.append(Integer.toHexString(mWhat));
+ sb.append(")");
+ if ( ! TextUtils.isEmpty(mInfo)) {
+ sb.append(" ");
+ sb.append(mInfo);
}
+ return sb.toString();
}
}
@@ -542,9 +562,9 @@
private int mCount = 0;
/**
- * Constructor
+ * private constructor use add
*/
- ProcessedMessages() {
+ private ProcessedMessages() {
}
/**
@@ -599,22 +619,23 @@
/**
* Add a processed message.
*
- * @param message
+ * @param msg
+ * @param messageInfo to be stored
* @param state that handled the message
* @param orgState is the first state the received the message but
* did not processes the message.
*/
- void add(Message message, State state, State orgState) {
+ void add(Message msg, String messageInfo, State state, State orgState) {
mCount += 1;
if (mMessages.size() < mMaxSize) {
- mMessages.add(new ProcessedMessageInfo(message, state, orgState));
+ mMessages.add(new ProcessedMessageInfo(msg, messageInfo, state, orgState));
} else {
ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
mOldestIndex += 1;
if (mOldestIndex >= mMaxSize) {
mOldestIndex = 0;
}
- pmi.update(message, state, orgState);
+ pmi.update(msg, messageInfo, state, orgState);
}
}
}
@@ -894,11 +915,14 @@
/**
* Record that we processed the message
*/
- if (curStateInfo != null) {
- State orgState = mStateStack[mStateStackTopIndex].state;
- mProcessedMessages.add(msg, curStateInfo.state, orgState);
- } else {
- mProcessedMessages.add(msg, null, null);
+ if (mSm.recordProcessedMessage(msg)) {
+ if (curStateInfo != null) {
+ State orgState = mStateStack[mStateStackTopIndex].state;
+ mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,
+ orgState);
+ } else {
+ mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
+ }
}
}
@@ -1546,6 +1570,24 @@
}
/**
+ * @return true if msg should be saved in ProcessedMessage, default is true.
+ */
+ protected boolean recordProcessedMessage(Message msg) {
+ return true;
+ }
+
+ /**
+ * Return message info to be logged by ProcessedMessageInfo, default
+ * is an empty string. Override if additional information is desired.
+ *
+ * @param msg that was processed
+ * @return information to be logged as a String
+ */
+ protected String getMessageInfo(Message msg) {
+ return "";
+ }
+
+ /**
* @return if debugging is enabled
*/
public boolean isDbg() {
@@ -1577,4 +1619,21 @@
/** Send the complete construction message */
mSmHandler.completeConstruction();
}
+
+ /**
+ * Dump the current state.
+ *
+ * @param fd
+ * @param pw
+ * @param args
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(getName() + ":");
+ pw.println(" total messages=" + getProcessedMessagesCount());
+ for (int i=0; i < getProcessedMessagesSize(); i++) {
+ pw.printf(" msg[%d]: %s\n", i, getProcessedMessageInfo(i));
+ pw.flush();
+ }
+ pw.println("curState=" + getCurrentState().getName());
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 8c05459..7a6f7d9 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -246,6 +246,10 @@
mHomeLayout.setOnClickListener(mUpClickListener);
mHomeLayout.setClickable(true);
mHomeLayout.setFocusable(true);
+
+ if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
@Override
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 0524c25..1d6af90 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -21,6 +21,8 @@
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.FontMetricsInt;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManager.InputDeviceListener;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -32,7 +34,7 @@
import java.util.ArrayList;
-public class PointerLocationView extends View {
+public class PointerLocationView extends View implements InputDeviceListener {
private static final String TAG = "Pointer";
public static class PointerState {
@@ -82,6 +84,8 @@
private final int ESTIMATE_FUTURE_POINTS = 2;
private final float ESTIMATE_INTERVAL = 0.02f;
+ private final InputManager mIm;
+
private final ViewConfiguration mVC;
private final Paint mTextPaint;
private final Paint mTextBackgroundPaint;
@@ -108,6 +112,8 @@
super(c);
setFocusableInTouchMode(true);
+ mIm = (InputManager)c.getSystemService(Context.INPUT_SERVICE);
+
mVC = ViewConfiguration.get(c);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
@@ -139,18 +145,6 @@
mActivePointerId = 0;
mVelocity = VelocityTracker.obtain();
-
- logInputDeviceCapabilities();
- }
-
- private void logInputDeviceCapabilities() {
- int[] deviceIds = InputDevice.getDeviceIds();
- for (int i = 0; i < deviceIds.length; i++) {
- InputDevice device = InputDevice.getDevice(deviceIds[i]);
- if (device != null) {
- Log.i(TAG, device.toString());
- }
- }
}
public void setPrintCoords(boolean state) {
@@ -631,7 +625,53 @@
logMotionEvent("Trackball", event);
return true;
}
-
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mIm.registerInputDeviceListener(this, getHandler());
+ logInputDevices();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mIm.unregisterInputDeviceListener(this);
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ logInputDeviceState(deviceId, "Device Added");
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ logInputDeviceState(deviceId, "Device Changed");
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ logInputDeviceState(deviceId, "Device Removed");
+ }
+
+ private void logInputDevices() {
+ int[] deviceIds = InputDevice.getDeviceIds();
+ for (int i = 0; i < deviceIds.length; i++) {
+ logInputDeviceState(deviceIds[i], "Device Enumerated");
+ }
+ }
+
+ private void logInputDeviceState(int deviceId, String state) {
+ InputDevice device = mIm.getInputDevice(deviceId);
+ if (device != null) {
+ Log.i(TAG, state + ": " + device);
+ } else {
+ Log.i(TAG, state + ": " + deviceId);
+ }
+ }
+
// HACK
// A quick and dirty string builder implementation optimized for GC.
// Using String.format causes the application grind to a halt when
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 244b166..c48b974 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -35,6 +35,7 @@
static const char* const OutOfResourcesException =
"android/graphics/SurfaceTexture$OutOfResourcesException";
+static const char* const IllegalStateException = "java/lang/IllegalStateException";
const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
struct fields_t {
@@ -212,10 +213,16 @@
surfaceTexture->setDefaultBufferSize(width, height);
}
-static jint SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
+static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
{
sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
- return surfaceTexture->updateTexImage();
+ status_t err = surfaceTexture->updateTexImage();
+ if (err == INVALID_OPERATION) {
+ jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
+ "logcat for details)");
+ } else if (err < 0) {
+ jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
+ }
}
static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
@@ -258,7 +265,7 @@
{"nativeInit", "(ILjava/lang/Object;Z)V", (void*)SurfaceTexture_init },
{"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
{"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
- {"nativeUpdateTexImage", "()I", (void*)SurfaceTexture_updateTexImage },
+ {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },
{"nativeDetachFromGLContext", "()I", (void*)SurfaceTexture_detachFromGLContext },
{"nativeAttachToGLContext", "(I)I", (void*)SurfaceTexture_attachToGLContext },
{"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix },
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 202abf6..9abfb3a 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -179,6 +179,6 @@
int register_android_hardware_SensorManager(JNIEnv *env)
{
- return jniRegisterNativeMethods(env, "android/hardware/SensorManager",
+ return jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 0ab659b..325fe26 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -31,6 +31,9 @@
namespace android {
+static const uint64_t VALUE_UNKNOWN = -1;
+static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all";
+
enum Tx_Rx {
TX,
RX
@@ -42,6 +45,21 @@
TCP_AND_UDP
};
+// NOTE: keep these in sync with TrafficStats.java
+enum IfaceStatType {
+ RX_BYTES = 0,
+ RX_PACKETS = 1,
+ TX_BYTES = 2,
+ TX_PACKETS = 3
+};
+
+struct IfaceStat {
+ uint64_t rxBytes;
+ uint64_t rxPackets;
+ uint64_t txBytes;
+ uint64_t txPackets;
+};
+
// Returns an ASCII decimal number read from the specified file, -1 on error.
static jlong readNumber(char const* filename) {
char buf[80];
@@ -63,130 +81,82 @@
return atoll(buf);
}
-static const char* mobile_iface_list[] = {
- "rmnet0",
- "rmnet1",
- "rmnet2",
- "rmnet3",
- "cdma_rmnet4",
- "ppp0",
- 0
-};
+static int parseIfaceStat(const char* iface, struct IfaceStat* stat) {
+ FILE *fp = fopen(IFACE_STAT_ALL, "r");
+ if (!fp) {
+ return errno;
+ }
-static jlong getAll(const char** iface_list, const char* what) {
+ char buffer[256];
+ char cur_iface[32];
+ int active;
+ uint64_t rxBytes, rxPackets, txBytes, txPackets, devRxBytes, devRxPackets, devTxBytes,
+ devTxPackets;
- char filename[80];
- int idx = 0;
- bool supported = false;
- jlong total = 0;
- while (iface_list[idx] != 0) {
-
- snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s",
- iface_list[idx], what);
- jlong number = readNumber(filename);
- if (number >= 0) {
- supported = true;
- total += number;
+ while (fgets(buffer, 256, fp) != NULL) {
+ if (sscanf(buffer, "%31s %d %llu %llu %llu %llu %llu %llu %llu %llu", cur_iface, &active,
+ &rxBytes, &rxPackets, &txBytes, &txPackets, &devRxBytes, &devRxPackets,
+ &devTxBytes, &devTxPackets) != 10) {
+ continue;
}
- idx++;
- }
- if (supported) return total;
- return -1;
-}
+ if (!iface || !strcmp(iface, cur_iface)) {
+ stat->rxBytes += rxBytes;
+ stat->rxPackets += rxPackets;
+ stat->txBytes += txBytes;
+ stat->txPackets += txPackets;
-// Returns the sum of numbers from the specified path under /sys/class/net/*,
-// -1 if no such file exists.
-static jlong readTotal(char const* suffix) {
- char filename[PATH_MAX] = "/sys/class/net/";
- DIR *dir = opendir(filename);
- if (dir == NULL) {
- ALOGE("Can't list %s: %s", filename, strerror(errno));
- return -1;
- }
-
- int len = strlen(filename);
- jlong total = -1;
- while (struct dirent *entry = readdir(dir)) {
- // Skip ., .., and localhost interfaces.
- if (entry->d_name[0] != '.' && strncmp(entry->d_name, "lo", 2) != 0) {
- strlcpy(filename + len, entry->d_name, sizeof(filename) - len);
- strlcat(filename, suffix, sizeof(filename));
- jlong num = readNumber(filename);
- if (num >= 0) total = total < 0 ? num : total + num;
+ if (active) {
+ stat->rxBytes += devRxBytes;
+ stat->rxPackets += devRxPackets;
+ stat->txBytes += devTxBytes;
+ stat->txPackets += devTxPackets;
+ }
}
}
- closedir(dir);
- return total;
+ fclose(fp);
+ return 0;
}
-// Mobile stats get accessed a lot more often than total stats.
-// Note the individual files can come and go at runtime, so we check
-// each file every time (rather than caching which ones exist).
+static uint64_t getIfaceStatType(const char* iface, IfaceStatType type) {
+ struct IfaceStat stat;
+ memset(&stat, 0, sizeof(IfaceStat));
-static jlong getMobileTxPackets(JNIEnv* env, jobject clazz) {
- return getAll(mobile_iface_list, "tx_packets");
-}
-
-static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) {
- return getAll(mobile_iface_list, "rx_packets");
-}
-
-static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) {
- return getAll(mobile_iface_list, "tx_bytes");
-}
-
-static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) {
- return getAll(mobile_iface_list, "rx_bytes");
-}
-
-static jlong getData(JNIEnv* env, const char* what, jstring javaInterface) {
- ScopedUtfChars interface(env, javaInterface);
- if (interface.c_str() == NULL) {
- return -1;
+ if (parseIfaceStat(iface, &stat)) {
+ return VALUE_UNKNOWN;
}
- char filename[80];
- snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interface.c_str(), what);
- return readNumber(filename);
+ switch (type) {
+ case RX_BYTES:
+ return stat.rxBytes;
+ case RX_PACKETS:
+ return stat.rxPackets;
+ case TX_BYTES:
+ return stat.txBytes;
+ case TX_PACKETS:
+ return stat.txPackets;
+ default:
+ return VALUE_UNKNOWN;
+ }
}
-static jlong getTxPackets(JNIEnv* env, jobject clazz, jstring interface) {
- return getData(env, "tx_packets", interface);
+static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
+ return getIfaceStatType(NULL, (IfaceStatType) type);
}
-static jlong getRxPackets(JNIEnv* env, jobject clazz, jstring interface) {
- return getData(env, "rx_packets", interface);
+static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
+ struct IfaceStat stat;
+ const char* ifaceChars = env->GetStringUTFChars(iface, NULL);
+ if (ifaceChars) {
+ uint64_t stat = getIfaceStatType(ifaceChars, (IfaceStatType) type);
+ env->ReleaseStringUTFChars(iface, ifaceChars);
+ return stat;
+ } else {
+ return VALUE_UNKNOWN;
+ }
}
-static jlong getTxBytes(JNIEnv* env, jobject clazz, jstring interface) {
- return getData(env, "tx_bytes", interface);
-}
-
-static jlong getRxBytes(JNIEnv* env, jobject clazz, jstring interface) {
- return getData(env, "rx_bytes", interface);
-}
-
-
-// Total stats are read less often, so we're willing to put up
-// with listing the directory and concatenating filenames.
-
-static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) {
- return readTotal("/statistics/tx_packets");
-}
-
-static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) {
- return readTotal("/statistics/rx_packets");
-}
-
-static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) {
- return readTotal("/statistics/tx_bytes");
-}
-
-static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) {
- return readTotal("/statistics/rx_bytes");
-}
// Per-UID stats require reading from a constructed filename.
@@ -323,18 +293,8 @@
}
static JNINativeMethod gMethods[] = {
- {"getMobileTxPackets", "()J", (void*) getMobileTxPackets},
- {"getMobileRxPackets", "()J", (void*) getMobileRxPackets},
- {"getMobileTxBytes", "()J", (void*) getMobileTxBytes},
- {"getMobileRxBytes", "()J", (void*) getMobileRxBytes},
- {"getTxPackets", "(Ljava/lang/String;)J", (void*) getTxPackets},
- {"getRxPackets", "(Ljava/lang/String;)J", (void*) getRxPackets},
- {"getTxBytes", "(Ljava/lang/String;)J", (void*) getTxBytes},
- {"getRxBytes", "(Ljava/lang/String;)J", (void*) getRxBytes},
- {"getTotalTxPackets", "()J", (void*) getTotalTxPackets},
- {"getTotalRxPackets", "()J", (void*) getTotalRxPackets},
- {"getTotalTxBytes", "()J", (void*) getTotalTxBytes},
- {"getTotalRxBytes", "()J", (void*) getTotalRxBytes},
+ {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
+ {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
/* Per-UID Stats */
{"getUidTxBytes", "(I)J", (void*) getUidTxBytes},
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index d7d476a..5cb172b 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -54,9 +54,10 @@
}
ScopedLocalRef<jobject> inputDeviceObj(env, env->NewObject(gInputDeviceClassInfo.clazz,
- gInputDeviceClassInfo.ctor, deviceInfo.getId(), nameObj.get(),
- descriptorObj.get(), deviceInfo.getSources(), deviceInfo.getKeyboardType(),
- kcmObj.get()));
+ gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
+ nameObj.get(), descriptorObj.get(),
+ deviceInfo.getSources(), deviceInfo.getKeyboardType(),
+ kcmObj.get(), deviceInfo.hasVibrator()));
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (size_t i = 0; i < ranges.size(); i++) {
@@ -86,7 +87,7 @@
gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
- "<init>", "(ILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;)V");
+ "<init>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;Z)V");
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFF)V");
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 6b06456..78b7d29 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -20,8 +20,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000" android:shareInterpolator="false">
<scale
- android:fromXScale="0.95" android:toXScale="1.0"
- android:fromYScale="0.95" android:toYScale="1.0"
+ android:fromXScale="0.90" android:toXScale="1.0"
+ android:fromYScale="0.90" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_cubic"
@@ -30,7 +30,7 @@
<alpha
android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@interpolator/decelerate_quad"
+ android:interpolator="@interpolator/decelerate_quint"
android:startOffset="@android:integer/config_shortAnimTime"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/drawable-nodpi/view_accessibility_focused.9.png b/core/res/res/drawable-nodpi/view_accessibility_focused.9.png
new file mode 100644
index 0000000..f03f575
--- /dev/null
+++ b/core/res/res/drawable-nodpi/view_accessibility_focused.9.png
Binary files differ
diff --git a/core/res/res/raw/accessibility_gestures.bin b/core/res/res/raw/accessibility_gestures.bin
new file mode 100644
index 0000000..1f95e56
--- /dev/null
+++ b/core/res/res/raw/accessibility_gestures.bin
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9528a7a..d299436 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Laat die program toe om die skerm se rotasie te eniger tyd te verander. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"verander wyserspoed"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Laat die program toe om die muis of stuurpaneel se wyserspoed te eniger tyd te verander. Dit moet nooit vir normale programme nodig wees nie."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"verander sleutelborduitleg"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Laat die program toe om die uitleg van die sleutelbord te verander. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"stuur Linux-seine na programme"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Laat die program toe om te versoek dat die voorsiende sein na alle aanhoudende prosesse gestuur word."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"laat program altyd loop"</string>
@@ -1009,11 +1007,17 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer die vereiste PIN in:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Voeg karakter in"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Onbekend program"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Stuur SMS-boodskappe"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"\'n Groot aantal SMS-boodskappe word gestuur. Raak OK om voort te gaan, of Kanselleer om op te hou stuur."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Kanselleer"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie program toelaat om voort te gaan om boodskappe te stuur?"</string>
+ <string name="sms_control_yes" msgid="3663725993855816807">"Laat toe"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Weier"</string>
+ <string name="sms_short_code_confirm_title" msgid="1666863092640877318">"Stuur SMS na kortkode?"</string>
+ <string name="sms_premium_short_code_confirm_title" msgid="3811263856304367838">"Stuur \'n premium SMS?"</string>
+ <string name="sms_short_code_confirm_message" msgid="5616409294907295407">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wil graag \'n SMS stuur aan <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, wat lyk asof dit \'n SMS-kortkode is.<p>Die stuur van SMS\'e na sommige kortkodes kan veroorsaak dat jou selfoonrekening gedebiteer word vir premium dienste.<p>Wil jy hierdie program toelaat om die boodskap te stuur?"</string>
+ <string name="sms_premium_short_code_confirm_message" msgid="6214083016284738667">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wil graag \'n SMS stuur aan <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, wat \'n betaalde SMS-kortkode is.<p><b>As jy \'n boodskap na hierdie bestemming stuur, sal jou selfoonrekening gedebiteer word vir betaalde dienste.</b><p>Wil jy hierdie program toelaat om die boodskap te stuur?"</string>
+ <string name="sms_short_code_confirm_allow" msgid="8957573662645722940">"Stuur boodskap"</string>
+ <string name="sms_short_code_confirm_deny" msgid="6374609298084435887">"Moenie stuur nie"</string>
+ <string name="sms_short_code_confirm_report" msgid="2588793956061677070">"Gee kwaadwillige program aan"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kaart verwyder"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Die mobielnetwerk sal nie beskikbaar wees nie totdat jy weer begin met \'n geldige SIM-kaart."</string>
<string name="sim_done_button" msgid="827949989369963775">"Klaar"</string>
@@ -1063,10 +1067,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Raak om USB-ontfouting te deaktiveer."</string>
<string name="select_input_method" msgid="4653387336791222978">"Kies invoermetode"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Stel invoermetodes op"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fisiese sleutelbord"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardeware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidate"</u></string>
@@ -1193,14 +1195,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Verminder dag"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Vermeerder jaar"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Verminder jaar"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"gekontroleer"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nie gekontroleer nie"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"gekies"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie gekies nie"</string>
- <string name="switch_on" msgid="551417728476977311">"aan"</string>
- <string name="switch_off" msgid="7249798614327155088">"af"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"gedruk"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nie gedruk nie"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselleer"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Vee uit"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 747b12d..b2714dd 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"በማንኛውም ጊዜ የማሳያውን መሽከርከር ለመለወጥ ለመተግበሪያው ይፈቅዳሉ፡፡ ለተለመዱ መተግበሪያዎች አያስፈልግም፡፡"</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"የጠቋሚ ፍጥነት ለውጥ"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"መዳፊት ወይም ዱካ መከተያ ጠቋሚ ፍጥነትን በማንኛውም ጊዜ ለመለወጥ ለመተግበሪያው ይፈቅዳሉ፡፡ ለመደበኛ መተግበሪያዎች መቼም ቢሆን አያስፈልግም፡፡"</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"የቁልፍ ሰሌዳ አቀማመጥ ቀይር"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"መተግበሪያው የቁልፍ ሰሌዳ አቀማመጡን እንዲቀይር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች መቼም ቢሆን ሊያስፈልግ አይገባም።"</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"ወደ መተግበሪያዎችን የLinux ምልክቶች ላክ"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ለሁሉም ተከታታይ ሂደቶች ልከው የሚያቀርቧቸው ሲግናሎችን ለመጠየቅ ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"ትግበራ ሁልጊዜ አሂድ ላይ አድርግ"</string>
@@ -1009,11 +1007,17 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"የሚፈለገውን ፒን ተይብ፦"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ፒን፦"</string>
<string name="select_character" msgid="3365550120617701745">"ቁምፊ አስገባ"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"ያልታወቀ መተግበሪያ"</string>
<string name="sms_control_title" msgid="7296612781128917719">"የSMS መልዕክቶች መበላክ ላይ"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"በጣም ብዙ የ SMS መልዕክቶቸ ተልከዋል።ለመቀጠል \"እሺ \" ፣ ወይም መላክ ለማቆም\"ተው \" ምረጥ።"</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"እሺ"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"ይቅር"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ቁጥሩ ብዙ የሆኑ የኤስ.ኤም.ኤስ. መልዕቶችን እየላከ ነው። ይሄ መተግበሪያ መልዕክቶችን መላኩን እንዲቀጥል መፍቀድ ትፈልጋለህ?"</string>
+ <string name="sms_control_yes" msgid="3663725993855816807">"ፍቀድ"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"ከልክል"</string>
+ <string name="sms_short_code_confirm_title" msgid="1666863092640877318">"ኤስ.ኤም.ኤስ. ለአጭር ኮድ ይላክ?"</string>
+ <string name="sms_premium_short_code_confirm_title" msgid="3811263856304367838">"ከፍ ያለ ኤስ.ኤም.ኤስ. ይላክ?"</string>
+ <string name="sms_short_code_confirm_message" msgid="5616409294907295407">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> የጽሑፍ መልዕክት ለ<b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> መላክ ይፈልጋል፣ ይሄ ደግሞ የኤስ.ኤም.ኤስ. አጭር ኮድ ሆኖ ተገኝቷል።<p>የጽሑፍ መልዕክቶች ለሆኑ አጭር ኮዶች መላክ የተንቀሳቃሽ መለያህ ከፍ ላሉ አገልግሎቶች ሊያስከፍለው ይችላል።<p>ይሄ መተግበሪያ መልዕክቱ እንዲልክ መፍቀድ ትፈልጋለህ?"</string>
+ <string name="sms_premium_short_code_confirm_message" msgid="6214083016284738667">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> የጽሑፍ መልዕክት ለ<b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> መላክ ይፈልጋል፣ ይሄ ደግሞ ከፍ ያለ የኤስ.ኤም.ኤስ. አጭር ኮድ ነው።<p><b>መልዕክት ወደዚህ ቦታ መላክ የተንቀሳቃሽ መለያህ ከፍ ላሉ አገልግሎቶች ያስከፍለዋል።</b><p>ይሄ መተግበሪያ መልዕክቱ እንዲልክ መፍቀድ ትፈልጋለህ?"</string>
+ <string name="sms_short_code_confirm_allow" msgid="8957573662645722940">"መልዕክት ላክ"</string>
+ <string name="sms_short_code_confirm_deny" msgid="6374609298084435887">"አትላክ"</string>
+ <string name="sms_short_code_confirm_report" msgid="2588793956061677070">"ተንኮል አዘል መተግበሪያ ሪፖርት አድርግ"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM ካርድ ተወግዷል"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"በትክክል የገባ SIM ካርድ ድጋሚ እስኪያስጀምሩ የተንቀሳቃሽ ስልክ አውታረመረብ አይገኝም።"</string>
<string name="sim_done_button" msgid="827949989369963775">"ተከናውኗል"</string>
@@ -1063,10 +1067,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ማረሚያ ላለማንቃት ዳስስ።"</string>
<string name="select_input_method" msgid="4653387336791222978">"የግቤት ስልት ምረጥ"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"የግቤት ስልቶችን አዘጋጅ"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"የሚዳሰስ የቁልፍ ሰሌዳ"</string>
+ <string name="hardware" msgid="7517821086888990278">"ሃርድዌር"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ዕጩዎች"</u></string>
@@ -1193,14 +1195,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ቀን ቀንስ"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"ዓመት ጨምር"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ዓመት ቀንስ"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"ታይቷል"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"አልተፈተሸም"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"የተመረጠ"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"አልተመረጠም"</string>
- <string name="switch_on" msgid="551417728476977311">"በ:"</string>
- <string name="switch_off" msgid="7249798614327155088">"ውጪ"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"ተጭኗል"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"አልተጫነም።"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ተወው"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ሰርዝ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e95ea69..daef582 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"للسماح للتطبيق بتغيير تدوير الشاشة في أي وقت. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"تغيير سرعة المؤشر"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"للسماح للتطبيق بتغيير سرعة مؤشر الماوس أو لوحة التتبع في أي وقت. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"تغيير تنسيق لوحة مفاتيح"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"للسماح للتطبيق بتغيير تنسيق لوحة المفاتيح. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"إرسال إشارات Linux للتطبيقات"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"للسماح للتطبيق بطلب إرسال الإشارة المزوّدة لجميع العمليات المستمرة."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"تشغيل التطبيق دائمًا"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"اكتب رقم التعريف الشخصي المطلوب:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"رقم التعريف الشخصي:"</string>
<string name="select_character" msgid="3365550120617701745">"إدراج حرف"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"تطبيق غير معروف"</string>
<string name="sms_control_title" msgid="7296612781128917719">"إرسال رسائل قصيرة SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"تم إرسال عدد كبير من الرسائل القصيرة SMS. المس \"موافق\" للمتابعة، أو \"إلغاء\" لإيقاف الإرسال."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"موافق"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"إلغاء"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"تمت إزالة بطاقة SIM"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"لن تكون شبكة الجوال متاحة حتى تتم إعادة التشغيل وإدخال بطاقة SIM صالحة."</string>
<string name="sim_done_button" msgid="827949989369963775">"تم"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"المس لتعطيل تصحيح أخطاء USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"اختيار أسلوب الإدخال"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"إعداد أسلوب الإدخال"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"لوحة مفاتيح فعلية"</string>
+ <string name="hardware" msgid="7517821086888990278">"أجهزة"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789 أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"العناصر المرشحة"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"تقليل الأيام"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"زيادة الأعوام"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"تقليل الأعوام"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"تم التحديد"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"لم يتم التحديد"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"محدد"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"غير محدد"</string>
- <string name="switch_on" msgid="551417728476977311">"تشغيل"</string>
- <string name="switch_off" msgid="7249798614327155088">"إيقاف"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"مضغوط"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"غير مضغوط"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"إلغاء"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"حذف"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c78ecd9..c155537 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Дазваляе прыкладанням змяняць паварот экрана ў любы час. Не патрабуецца для звычайных прыкладанняў."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"змена хутк. перамяшч. ўказ."</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Дазваляе прыкладанням змяняць хуткасць курсору мышы або трэкпада ў любы час. Не патрабуецца для звычайных прыкладанняў."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"змяніць раскладку клавіятуры"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Дазваляе прыкладанню змяняць раскладку клавіятуры. Не патрабуецца для звычайных прыкладанняў."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"адправіць сігналы Linux да прыкладанняў"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Дазваляе прыкладанням запытваць адпраўку падаваемага сігнала для ўсiх пастаянных працэсаў."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"прымусіць прыкладанне працаваць заўсёды"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Увядзіце патрэбны PIN-код:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код"</string>
<string name="select_character" msgid="3365550120617701745">"Уставіць сімвал"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Невядомае прыкладанне"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Адпраўка SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Адпраўляецца вялікая колькасць SMS-паведамленняў. Націсніце \"OK\", каб працягнуць, ці \"Адмена\", каб спыніць адпраўку."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"ОК"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Адмяніць"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-карта выдаленая"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Мабільная сетка будзе недаступная да перазагрузкі з дзеючай SIM-картай."</string>
<string name="sim_done_button" msgid="827949989369963775">"Гатова"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Націсніце, каб адключыць адладку USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Выберыце метад уводу"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Наладзіць метады ўводу"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Фізічная клавіятура"</string>
+ <string name="hardware" msgid="7517821086888990278">"Апар. ср."</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШ\'ЫЬЭЮЯ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандыдат."</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Паменшыць лічбу дня"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Павялічыць лічбу года"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Паменшыць лічбу года"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"пастаўлены"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"не пастаўлены"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"абрана"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"не абрана"</string>
- <string name="switch_on" msgid="551417728476977311">"укл."</string>
- <string name="switch_off" msgid="7249798614327155088">"адключаны"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"націснутая"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не націснутая"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Адмена"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Выдаліць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8bcbec3..7860382 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Въведете задължителния ПИН:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ПИН:"</string>
<string name="select_character" msgid="3365550120617701745">"Вмъкване на знак"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Неизвестно приложение"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Изпращане на SMS съобщения"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Изпращат се голям брой SMS съобщения. Докоснете „OK“, за да продължите, или „Отказ“, за да спрете изпращането."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Отказ"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM картата е премахната"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Няма да имате достъп до мобилната мрежа, докато не рестартирате с поставена валидна SIM карта."</string>
<string name="sim_done_button" msgid="827949989369963775">"Готово"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Намаляване на дните"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Увеличаване на годините"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Намаляване на годините"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"отметнато"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"не е отметнато"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"избрано"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"не е избрано"</string>
- <string name="switch_on" msgid="551417728476977311">"включено"</string>
- <string name="switch_off" msgid="7249798614327155088">"изключено"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"натиснато"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не е натиснато"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отказ"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Изтриване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fdae718..e266a85 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permet que l\'aplicació canviï el gir de la pantalla en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"canvi de velocitat del punter"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permet que l\'aplicació canviï la velocitat del punter del ratolí o del ratolí tàctil en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"canvia la disposició del teclat"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permet que l\'aplicació canviï la disposició del teclat. En principi mai no serà necessari per a les aplicacions normals."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"envia senyals Linux a les aplicacions"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Permet que l\'aplicació sol·liciti que el senyal subministrat s\'enviï a tots els processos persistents."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"fes que l\'aplicació s\'executi sempre"</string>
@@ -1009,11 +1007,17 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introdueix el PIN sol·licitat:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Insereix un caràcter"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicació desconeguda"</string>
<string name="sms_control_title" msgid="7296612781128917719">"S\'estan enviant missatges SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"S\'estan enviant molts missatges SMS. Toca D\'acord per continuar o Cancel·la per aturar l\'enviament."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"D\'acord"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancel·la"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> està enviant molts missatges SMS. Vols permetre que aquesta aplicació continuï enviant missatges?"</string>
+ <string name="sms_control_yes" msgid="3663725993855816807">"Permet"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Denega"</string>
+ <string name="sms_short_code_confirm_title" msgid="1666863092640877318">"Vols enviar SMS a codi curt?"</string>
+ <string name="sms_premium_short_code_confirm_title" msgid="3811263856304367838">"Vols enviar el SMS prèmium?"</string>
+ <string name="sms_short_code_confirm_message" msgid="5616409294907295407">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> vol enviar un missatge de text a <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, que sembla que és un codi SMS curt.<p>Si envies missatges de text a codis curts, pot ser que es carreguin serveis prèmium al teu compte mòbil.<p>Vols permetre que aquesta aplicació enviï el missatge?"</string>
+ <string name="sms_premium_short_code_confirm_message" msgid="6214083016284738667">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> vol enviar un missatge de text a <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, que és un codi curt SMS prèmium.<p><b>Si envies un missatge a aquesta destinació, es carregaran els serveis prèmium al teu compte mòbil.</b><p>Vols permetre que aquesta aplicació enviï el missatge?"</string>
+ <string name="sms_short_code_confirm_allow" msgid="8957573662645722940">"Envia el missatge"</string>
+ <string name="sms_short_code_confirm_deny" msgid="6374609298084435887">"No enviïs"</string>
+ <string name="sms_short_code_confirm_report" msgid="2588793956061677070">"Informa d\'una aplic. maliciosa"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"Extracció de la targeta SIM"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"La xarxa de telefonia mòbil no estarà disponible fins que no reiniciïs amb una targeta SIM vàlida inserida."</string>
<string name="sim_done_button" msgid="827949989369963775">"Fet"</string>
@@ -1063,10 +1067,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca-ho per desactivar la depuració USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Selecció de mètodes d\'introducció"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configura els mètodes d\'entrada"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclat físic"</string>
+ <string name="hardware" msgid="7517821086888990278">"Maquinari"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1193,14 +1195,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Fes disminuir el dia"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Fes augmentar l\'any"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Fes disminuir l\'any"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"marcat"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"no marcat"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionat"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"no seleccionat"</string>
- <string name="switch_on" msgid="551417728476977311">"activat"</string>
- <string name="switch_off" msgid="7249798614327155088">"desactivat"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"premut"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"no premut"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel·la"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Suprimeix"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 98b5771..33f91e8 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadejte požadovaný kód PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Vkládání znaků"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznámá aplikace"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Odesílání zpráv SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Odesíláte velký počet zpráv SMS. Dotkněte se možnosti OK, chcete-li pokračovat, nebo možnosti Zrušit, chcete-li odesílání ukončit."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Zrušit"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Karta SIM odebrána"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilní síť bude dostupná až poté, co vložíte platnou kartu SIM a restartujete zařízení."</string>
<string name="sim_done_button" msgid="827949989369963775">"Hotovo"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrat den"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Přidat rok"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrat rok"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"zaškrtnuto"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nezaškrtnuto"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"Vybráno"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"Nevybráno"</string>
- <string name="switch_on" msgid="551417728476977311">"zapnuto"</string>
- <string name="switch_off" msgid="7249798614327155088">"vypnuto"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"stisknuto"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nestisknuto"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušit"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Smazat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a3c7474..c5b0e17 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Tillader, at appen kan ændre skærmretningen på et hvilket som helst tidspunkt. Bør aldrig være nødvendigt for almindelige apps."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"ændre markørens hastighed"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Tillader, at appen til enhver tid kan ændre musemarkørens hastighed. Bør aldrig være nødvendigt for normale apps."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"skift tastaturlayout"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Tillader, at appen må ændre tastaturlayoutet. Bør aldrig være nødvendig for normale apps."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"sende Linux-signaler til apps"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Tillader, at appen kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"sørge for, at appen altid kører"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv den påkrævede pinkode:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pinkode:"</string>
<string name="select_character" msgid="3365550120617701745">"Indsæt tegn"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ukendt app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Sender sms-beskeder"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Der sendes et stort antal sms-beskeder. Vælg \"OK\" for at fortsætte eller \"Annuller\" for at stoppe afsendelsen."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Annuller"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kort blev fjernet"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Det mobile netværk er utilgængeligt, indtil du genstarter med et gyldigt SIM-kort."</string>
<string name="sim_done_button" msgid="827949989369963775">"Udfør"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tryk for at deaktivere USB-fejlretning."</string>
<string name="select_input_method" msgid="4653387336791222978">"Vælg inputmetode"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Konfigurer inputmetoder"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fysisk tastatur"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tidligere dag"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Senere år"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Tidligere år"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"markeret"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"ikke markeret"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"udvalgt"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ikke valgt"</string>
- <string name="switch_on" msgid="551417728476977311">"tændt"</string>
- <string name="switch_off" msgid="7249798614327155088">"slukket"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"trykket på"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ikke trykket på"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuller"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ae889201..5a24cb3 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Ermöglicht der App, die Bildschirmdrehung jederzeit zu ändern. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"Zeigergeschwindigkeit ändern"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Ermöglicht der App, jederzeit die Geschwindigkeit des Maus- bzw. Touchpad-Zeigers zu ändern. Sollte nie für normale Apps benötigt werden."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"Tastaturlayout ändern"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Ermöglicht der App, das Tastaturlayout zu ändern. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linux-Signale an Apps senden"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Ermöglicht der App, das Senden des gelieferten Signals an alle andauernden Prozesse zu fordern"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"App permanent ausführen"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Geben Sie die erforderliche PIN ein:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Zeichen einfügen"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Unbekannte App"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Kurznachrichten werden gesendet"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Es wird eine große Anzahl an SMS versendet. Tippen Sie zum Fortfahren auf \"OK\" oder tippen Sie auf \"Abbrechen\", um den Sendevorgang zu beenden."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Abbrechen"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-Karte entfernt"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Das Mobilfunknetz ist erst wieder verfügbar, wenn Sie einen Neustart mit einer gültigen SIM-Karte durchführen."</string>
<string name="sim_done_button" msgid="827949989369963775">"Fertig"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Zum Deaktivieren von USB-Debugging tippen"</string>
<string name="select_input_method" msgid="4653387336791222978">"Eingabemethode wählen"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Eingabemethoden einrichten"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Physische Tastatur"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tag verringern"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Jahr verlängern"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Jahr verringern"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"Aktiviert"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"Nicht aktiviert"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"Ausgewählt"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"Nicht ausgewählt"</string>
- <string name="switch_on" msgid="551417728476977311">"An"</string>
- <string name="switch_off" msgid="7249798614327155088">"Aus"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"Gedrückt"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"Nicht gedrückt"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Abbrechen"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Löschen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ce49a84..7c951cc 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Επιτρέπει στην εφαρμογή την αλλαγή της περιστροφής της οθόνης ανά πάσα στιγμή. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"αλλαγή ταχύτητας δείκτη"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Επιτρέπει στην εφαρμογή την αλλαγή της ταχύτητας του δείκτη του ποντικιού ή της επιφάνειας αφής ανά πάσα στιγμή. Δεν πρέπει να χρησιμοποιείται από συνήθεις εφαρμογές."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"αλλαγή διάταξης πληκτρολογίου"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Επιτρέπει στην εφαρμογή την αλλαγή της διάταξης πληκτρολογίου. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"αποστολή σημάτων Linux σε εφαρμογές"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Επιτρέπει στην εφαρμογή την αποστολή αιτήματος για την αποστολή του παρεχόμενου σήματος σε όλες τις υπάρχουσες διαδικασίες."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"να εκτελείται συνεχώς η εφαρμογή"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Εισαγωγή χαρακτήρα"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Άγνωστη εφαρμογή"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Αποστολή μηνυμάτων SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Αποστέλλεται μεγάλος αριθμός μηνυμάτων SMS. Αγγίξτε το στοιχείο \"OK\" για συνέχεια, ή το στοιχείο \"Ακύρωση\" για διακοπή της αποστολής."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Ακύρωση"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Η κάρτα SIM αφαιρέθηκε"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Το δίκτυο κινητής τηλεφωνίας δεν θα είναι διαθέσιμο μέχρι να κάνετε επανεκκίνηση αφού τοποθετήσετε μια έγκυρη κάρτα SIM."</string>
<string name="sim_done_button" msgid="827949989369963775">"Τέλος"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Αγγίξτε για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Επιλογή μεθόδου εισόδου"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Ρύθμιση μεθόδων εισαγωγής"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Φυσικό πληκτρολόγιο"</string>
+ <string name="hardware" msgid="7517821086888990278">"Υλικό"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"υποψήφιοι"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Μείωση ημέρας"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Αύξηση έτους"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Μείωση έτους"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"ελέγχθηκε"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"δεν επιλέχθηκε"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"επιλεγμένο"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"δεν έχει επιλεγεί"</string>
- <string name="switch_on" msgid="551417728476977311">"ενεργοποίηση"</string>
- <string name="switch_off" msgid="7249798614327155088">"απενεργοποιημένη"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"πατήθηκε"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"δεν πατήθηκε"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ακύρωση"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Διαγραφή"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 38b69fe..208e96a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Allows the app to change the rotation of the screen at any time. Should never be needed for normal apps."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"change pointer speed"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Allows the app to change the mouse or touch pad pointer speed at any time. Should never be needed for normal apps."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"change keyboard layout"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Allows the app to change the keyboard layout. Should never be needed for normal apps."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"send Linux signals to apps"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Allows the app to request that the supplied signal be sent to all persistent processes."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"make app always run"</string>
@@ -1009,11 +1007,17 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Type the required PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Insert character"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Unknown app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Sending SMS messages"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"A large number of SMS messages are being sent. Touch OK to continue or Cancel to stop sending."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancel"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> is sending a large number of SMS messages. Do you want to allow this app to continue sending messages?"</string>
+ <string name="sms_control_yes" msgid="3663725993855816807">"Allow"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Deny"</string>
+ <string name="sms_short_code_confirm_title" msgid="1666863092640877318">"Send SMS to short code?"</string>
+ <string name="sms_premium_short_code_confirm_title" msgid="3811263856304367838">"Send premium SMS?"</string>
+ <string name="sms_short_code_confirm_message" msgid="5616409294907295407">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, which appears to be an SMS short code.<p>Sending text messages to some short codes may cause your mobile account to be billed for premium services.<p>do you want to allow this app to send the message?"</string>
+ <string name="sms_premium_short_code_confirm_message" msgid="6214083016284738667">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, which is a premium SMS short code.<p><b>Sending a message to this destination will cause your mobile account to be billed for premium services.</b><p>Do you want to allow this app to send the message?"</string>
+ <string name="sms_short_code_confirm_allow" msgid="8957573662645722940">"Send message"</string>
+ <string name="sms_short_code_confirm_deny" msgid="6374609298084435887">"Don\'t send"</string>
+ <string name="sms_short_code_confirm_report" msgid="2588793956061677070">"Report malicious app"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM card removed"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"The mobile network will be unavailable until you restart with a valid SIM card inserted."</string>
<string name="sim_done_button" msgid="827949989369963775">"Done"</string>
@@ -1063,10 +1067,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Touch to disable USB debugging."</string>
<string name="select_input_method" msgid="4653387336791222978">"Choose input method"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Set up input methods"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Physical keyboard"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
@@ -1193,14 +1195,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Decrease day"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Increase year"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Decrease year"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"ticked"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"not ticked"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"selected"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"not selected"</string>
- <string name="switch_on" msgid="551417728476977311">"on"</string>
- <string name="switch_off" msgid="7249798614327155088">"off"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"pressed"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"not pressed"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d400734..6f1ee53 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permite que la aplicación cambie la rotación de la pantalla en cualquier momento. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"cambiar velocidad del puntero"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permite que la aplicación cambie la velocidad del puntero del mouse o el trackpad en cualquier momento. Las aplicaciones normales no deben utilizar este permiso."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"Cambiar el diseño del teclado"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permite que la aplicación cambie el diseño del teclado. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"enviar señales de Linux a las aplicaciones"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"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="8841113627955563938">"hacer que la aplicación se ejecute siempre"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Insertar caracteres"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicación desconocida"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Se está enviando una gran cantidad de mensajes SMS. Toca \"Aceptar\" para continuar o \"Cancelar\" para detener el envío."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Tarjeta SIM eliminada"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"La red para celulares no estará disponible hasta que reinicies, luego de insertar una tarjeta SIM válida."</string>
<string name="sim_done_button" msgid="827949989369963775">"Finalizado"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca para desactivar la depuración de USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Selecciona el método de introducción"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configurar métodos de introducción"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclado físico"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir día"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"marcado"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"no marcado"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionado"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"No se ha seleccionado."</string>
- <string name="switch_on" msgid="551417728476977311">"Activado"</string>
- <string name="switch_off" msgid="7249798614327155088">"Desactivado"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"presionado"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"sin presionar"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6852184..95e1152 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permite que la aplicación cambie la rotación de la pantalla en cualquier momento. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"cambiar velocidad del puntero"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permite que la aplicación modifique la velocidad del puntero del ratón o del trackpad en cualquier momento. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"cambiar el diseño del teclado"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permite que la aplicación cambie el diseño del teclado. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"enviar señales Linux a aplicaciones"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"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="8841113627955563938">"hacer que la aplicación se ejecute siempre"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Insertar carácter"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicación desconocida"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS..."</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Se va a enviar un número elevado de mensajes SMS. Selecciona Aceptar para continuar o Cancelar para detener el envío."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Tarjeta SIM eliminada"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"La red móvil no estará disponible hasta que reinicies el dispositivo con una tarjeta SIM válida."</string>
<string name="sim_done_button" msgid="827949989369963775">"Listo"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tocar para inhabilitar la depuración USB"</string>
<string name="select_input_method" msgid="4653387336791222978">"Seleccionar método de introducción"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Ajustar métodos de introducción"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclado físico"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir días"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"seleccionado"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"no seleccionado"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"seleccionado"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"no seleccionado"</string>
- <string name="switch_on" msgid="551417728476977311">"activado"</string>
- <string name="switch_off" msgid="7249798614327155088">"desactivado"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"pulsado"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"sin pulsar"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 737ab21..a72691f 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Võimaldab rakendusel muuta ekraani pööramist mis tahes ajal. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"kursorikiiruse muutmine"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Võimaldab rakendusel muuta igal ajal hiire- või puutepadjakursori kiirust. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"klaviatuuri paigutuse muutmine"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Lubab rakendusel muuta klaviatuuri paigutust. Tavarakenduste puhul ei peaks kunagi vaja olema."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linuxi signaalide saatmine rakendustele"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Võimaldab rakendusel taotleda edastatud signaali saatmist kõigile püsiprotsessidele."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"Rakenduste pidev töös hoidmine"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Sisestage nõutav PIN-kood:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kood:"</string>
<string name="select_character" msgid="3365550120617701745">"Sisesta tähemärk"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Tundmatu rakendus"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS-sõnumite saatmine"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Saadetakse suur hulk SMS-sõnumid. Saatmise jätkamiseks valige OK, lõpetamiseks Tühista."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Tühista"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kaart eemaldatud"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobiilsidevõrk ei ole saadaval, kuni sisestate kehtiva SIM-kaardi ja taaskäivitate seadme."</string>
<string name="sim_done_button" msgid="827949989369963775">"Valmis"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Puudutage USB-silumise keelamiseks."</string>
<string name="select_input_method" msgid="4653387336791222978">"Valige sisestusmeetod"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Seadista sisestusmeetodid"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Füüsiline klaviatuur"</string>
+ <string name="hardware" msgid="7517821086888990278">"Riistvara"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaadid"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Päeva vähendamine"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aasta suurendamine"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Aasta vähendamine"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"märgitud"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"pole märgitud"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"valitud"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"pole valitud"</string>
- <string name="switch_on" msgid="551417728476977311">"sees"</string>
- <string name="switch_off" msgid="7249798614327155088">"väljas"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"vajutatud"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"pole vajutatud"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Tühista"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Kustuta"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 80b273b..d33fe74 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"به برنامه اجازه میدهد تا چرخش صفحه را هر وقت بخواهد تغییر دهد. برای برنامههای عادی نیاز نیست."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"تغییر سرعت اشاره گر"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"به برنامه اجازه میدهد تا سرعت ماوس و پد کنترل را هر وقت خواست تغییر دهد. برای برنامههای عادی نیاز نیست."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"تغییر چیدمان صفحه کلید"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"به برنامه اجازه میدهد تا چیدمان صفحه کلید را تغییر دهد. این کار هیچ گاه برای برنامههای عادی نیاز نیست."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"ارسال سیگنالهای Linux به برنامهها"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"به برنامه اجازه میدهد تا درخواست کند سیگنال ارائه شده به همه مراحل دائم ارسال شود."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"همیشه برنامه اجرا شود"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"پین لازم را تایپ کنید:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"پین:"</string>
<string name="select_character" msgid="3365550120617701745">"درج نویسه"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"برنامه ناشناخته"</string>
<string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"تعداد زیادی پیام کوتاه ارسال شده است. برای ادامه، \"تأیید\" را لمس کرده و برای توقف ارسال، \"لغو\" را لمس کنید."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"تأیید"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"لغو"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"سیم کارت برداشته شد"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"تا وقتی که با یک سیمکارت معتبر راهاندازی مجدد نکنید شبکه تلفن همراه غیر قابل دسترس خواهد بود."</string>
<string name="sim_done_button" msgid="827949989369963775">"انجام شد"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"غیرفعال کردن اشکال زدایی USB را لمس کنید."</string>
<string name="select_input_method" msgid="4653387336791222978">"انتخاب روش ورودی"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"تنظیم روشهای ورودی"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"صفحه کلید فیزیکی"</string>
+ <string name="hardware" msgid="7517821086888990278">"سختافزار"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"داوطلبین"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"کاهش روز"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"افزایش سال"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"کاهش سال"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"علامت زده"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"بدون علامت"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"انتخاب شد"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"انتخاب نشده"</string>
- <string name="switch_on" msgid="551417728476977311">"روشن"</string>
- <string name="switch_off" msgid="7249798614327155088">"خاموش"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"فشرده شد"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"فشرده نشد"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f011ec3..f03e1af 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Antaa sovelluksen muuttaa näytön kiertoa milloin tahansa. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"muuta osoittimen nopeutta"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Antaa sovelluksen muuttaa hiiren tai kosketuslevyn osoittimen nopeutta milloin tahansa. Ei tavallisten sovellusten käyttöön."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"vaihda näppäimistön asettelua"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Antaa sovelluksen muuttaa näppäimistön asettelua. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linux-signaalien lähettäminen sovelluksille"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Antaa sovelluksen pyytää, että tarjottu signaali lähetetään kaikille käynnissä oleville prosesseille."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"sovelluksen asettaminen aina käynnissä olevaksi"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Kirjoita pyydetty PIN-koodi:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-koodi:"</string>
<string name="select_character" msgid="3365550120617701745">"Lisää merkki"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Tuntematon sovellus"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Tekstiviestien lähettäminen"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Olet lähettämässä suurta määrää tekstiviestejä. Jatka koskettamalla OK tai peruuta lähetys valitsemalla Peruuta."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Peruuta"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kortti poistettu"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobiiliverkko ei ole käytettävissä, ennen kuin käynnistät uudelleen kelvollisella laitteeseen kytketyllä SIM-kortilla."</string>
<string name="sim_done_button" msgid="827949989369963775">"Valmis"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Poista USB-vianetsintä käytöstä koskettamalla tätä."</string>
<string name="select_input_method" msgid="4653387336791222978">"Valitse syöttötapa"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Määritä syöttötavat"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fyysinen näppäimistö"</string>
+ <string name="hardware" msgid="7517821086888990278">"Laitteisto"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaatit"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Vähennä päivien määrää."</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Lisää vuosien määrää."</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Vähennä vuosien määrää."</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"valittu"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"ei valittu"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"valittu"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ei valittu"</string>
- <string name="switch_on" msgid="551417728476977311">"käytössä"</string>
- <string name="switch_off" msgid="7249798614327155088">"pois käytöstä"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"painettu"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ei painettu"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Peruuta"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Poista"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7c78654..72ac5f4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permet à l\'application de changer l\'orientation de l\'écran à tout moment. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"changer la vitesse du pointeur"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permet à l\'application de modifier à tout moment la vitesse du pointeur de la souris ou du pavé tactile. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"changer disposition clavier"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permet à l\'application de changer la disposition du clavier. Les applications standards ne devraient pas nécessiter cette autorisation."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"envoyer des signaux Linux aux applications"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Permet à l\'application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"exécuter l\'application en continu"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Saisissez le code PIN requis :"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Code PIN :"</string>
<string name="select_character" msgid="3365550120617701745">"Insérer un caractère"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Application inconnue"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Envoi de messages SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Vous êtes sur le point d\'envoyer un grand nombre de SMS. Appuyez sur \"OK\" pour continuer ou sur \"Annuler\" pour interrompre l\'envoi."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Annuler"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Carte SIM retirée"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Le réseau mobile ne sera pas disponible avant le redémarrage avec une carte SIM valide insérée."</string>
<string name="sim_done_button" msgid="827949989369963775">"OK"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Appuyez pour désactiver le débogage USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Sélectionnez le mode de saisie"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configurer les modes de saisie"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Clavier physique"</string>
+ <string name="hardware" msgid="7517821086888990278">"Matériel"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Jour précédent"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Année suivante"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Année précédente"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"coché"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"non coché"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"sélectionné"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"non sélectionné"</string>
- <string name="switch_on" msgid="551417728476977311">"activé"</string>
- <string name="switch_off" msgid="7249798614327155088">"désactivé"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"sélectionné"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"non sélectionné"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuler"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Supprimer"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 1ee3d03..e94e286 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"एप्लिकेशन को किसी भी समय स्क्रीन का रोटेशन बदलने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"सूचक गति बदलें"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"एप्लिकेशन को माउस या ट्रैकपैड सूचक गति को किसी भी समय बदलने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"कीबोर्ड लेआउट बदलें"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"एप्लिकेशन को कीबोर्ड लेआउट बदलने देता है. सामान्य एप्लिकेशन के लिए कभी आवश्यक नहीं है."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"एप्लिकेशन को Linux सिग्नल भेजें"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"एप्लिकेशन को यह अनुरोध करने देता है कि दिया गया सिग्नल सभी जारी प्रक्रियाओं को भेजा जाए."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"एप्लिकेशन को हमेशा चलने वाला बनाएं"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"आवश्यक पिन लिखें:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"पिन:"</string>
<string name="select_character" msgid="3365550120617701745">"वर्ण सम्मिलित करें"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"अज्ञात एप्लिकेशन"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS संदेश भेज रहा है"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"बड़ी संख्या में SMS संदेश भेजे जा रहे हैं. जारी रखने के लिए ठीक स्पर्श करें, या भेजना बंद करने के लिए रद्द करें स्पर्श करें."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"ठीक"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"रद्द करें"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"सिमकार्ड निकाला गया"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"मान्य सिम कार्ड डालकर पुन: प्रारंभ करने तक मोबाइल नेटवर्क अनुपलब्ध रहेगा."</string>
<string name="sim_done_button" msgid="827949989369963775">"पूर्ण"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB डीबग करना अक्षम करने के लिए स्पर्श करें."</string>
<string name="select_input_method" msgid="4653387336791222978">"इनपुट पद्धति चुनें"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"इनपुट पद्धतियां सेट करें"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"भौतिक कीबोर्ड"</string>
+ <string name="hardware" msgid="7517821086888990278">"हार्डवेयर"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"उम्मीदवार"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"दिन कम करें"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"वर्ष बढ़ाएं"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"वर्ष कम करें"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"चेक किया गया"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"चेक नहीं किया गया"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"चयनित"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"चयनित नहीं"</string>
- <string name="switch_on" msgid="551417728476977311">"चालू"</string>
- <string name="switch_off" msgid="7249798614327155088">"बंद"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"दबाया गया"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"दबाया नहीं गया."</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रद्द करें"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"हटाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0023310..412bc61 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Upišite potreban PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Umetni znak"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nepoznata aplikacija"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Slanje SMS poruka"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Šalje se velika količina SMS poruka. Dodirnite \"U redu\" za nastavak ili \"Odustani\" za prekid slanja."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"U redu"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Odustani"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM kartica uklonjena"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilna mreža bit će nedostupna do ponovnog pokretanja s umetnutom važećom SIM karticom."</string>
<string name="sim_done_button" msgid="827949989369963775">"Gotovo"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Smanjenje dana"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povećanje godine"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Smanjenje godine"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"označeno"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nije označeno"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"odabran"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nije odabrano"</string>
- <string name="switch_on" msgid="551417728476977311">"uključeno"</string>
- <string name="switch_off" msgid="7249798614327155088">"isključeno"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"pritisnut"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nije pritisnut"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Odustani"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 4ae52d5..c8f039f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Lehetővé teszi az alkalmazás számára a képernyő elforgatásának bármikori módosítását. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"mutató sebességének módosítása"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Lehetővé teszi az alkalmazás számára, hogy bármikor módosítsa az egér vagy az érintőpad mutatójának sebességét. Normál alkalmazásoknak soha nem lehet rá szükségük."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"billentyűzetkiosztás módosítása"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a billentyűzetkiosztást. Normál alkalmazásoknak alapesetben nem lehet szükségük rá."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linux-jelek küldése az alkalmazásoknak"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"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="8841113627955563938">"az alkalmazás állandó futtatása"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string>
<string name="select_character" msgid="3365550120617701745">"Karakter beszúrása"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ismeretlen alkalmazás"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS-ek küldése"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Nagy számú SMS-t kíván elküldeni. A folytatáshoz érintse meg az \"OK\", a küldés leállításához a \"Mégse\" lehetőséget."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Mégse"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kártya eltávolítva"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"A mobilhálózat nem lesz elérhető, amíg újra nem indítja egy érvényes SIM kártya behelyezése után."</string>
<string name="sim_done_button" msgid="827949989369963775">"Kész"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Érintse meg az USB hibakeresés kikapcsolásához."</string>
<string name="select_input_method" msgid="4653387336791222978">"Beviteli mód kiválasztása"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Beviteli módok beállítása"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fizikai billentyűzet"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardver"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"jelöltek"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Dátum értékének csökkentése"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Év értékének növelése"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Év értékének csökkentése"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"bejelölve"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nincs bejelölve"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"bejelölve"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nincs kiválasztva"</string>
- <string name="switch_on" msgid="551417728476977311">"be"</string>
- <string name="switch_off" msgid="7249798614327155088">"kikapcsolva"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"megnyomva"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nincs megnyomva"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Mégse"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bd6294b..03dac8a 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -109,7 +109,7 @@
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string>
<string name="fcComplete" msgid="3118848230966886575">"Kode fitur selesai."</string>
<string name="fcError" msgid="3327560126588500777">"Masalah sambungan atau kode fitur tidak valid."</string>
- <string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
+ <string name="httpErrorOk" msgid="1191919378083472204">"Oke"</string>
<string name="httpError" msgid="7956392511146698522">"Terjadi kesalahan jaringan."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"Tidak dapat menemukan URL."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Skema autentikasi situs tidak didukung."</string>
@@ -744,7 +744,7 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Mulai ulang"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Laman pada \"<xliff:g id="TITLE">%s</xliff:g>\" menyatakan:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Beranjak dari laman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh OK untuk melanjutkan atau Batal untuk tetap pada laman ini."</string>
+ <string name="js_dialog_before_unload" msgid="730366588032430474">"Beranjak dari laman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh Oke untuk melanjutkan atau Batal untuk tetap pada laman ini."</string>
<string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Isiotomatis"</string>
@@ -892,7 +892,7 @@
<string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video ini tidak valid untuk pengaliran ke perangkat ini."</string>
<string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat memutar video ini."</string>
- <string name="VideoView_error_button" msgid="2822238215100679592">"OK"</string>
+ <string name="VideoView_error_button" msgid="2822238215100679592">"Oke"</string>
<string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="noon" msgid="7245353528818587908">"tengah hari"</string>
<string name="Noon" msgid="3342127745230013127">"Tengah hari"</string>
@@ -916,9 +916,9 @@
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Ruang penyimpanan tinggal sedikit"</string>
<string name="low_internal_storage_view_text" product="tablet" msgid="4231085657068852042">"Ruang penyimpanan tablet semakin sedikit."</string>
<string name="low_internal_storage_view_text" product="default" msgid="635106544616378836">"Ruang penyimpanan ponsel tersisa sedikit."</string>
- <string name="ok" msgid="5970060430562524910">"OK"</string>
+ <string name="ok" msgid="5970060430562524910">"Oke"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
- <string name="yes" msgid="5362982303337969312">"OK"</string>
+ <string name="yes" msgid="5362982303337969312">"Oke"</string>
<string name="no" msgid="5141531044935541497">"Batal"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Perhatian"</string>
<string name="loading" msgid="7933681260296021180">"Memuat..."</string>
@@ -938,7 +938,7 @@
<string name="anr_activity_process" msgid="5776209883299089767">"Aktivitas <xliff:g id="ACTIVITY">%1$s</xliff:g> tidak menanggapi."\n\n"Anda ingin menutupnya?"</string>
<string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> tidak menanggapi. Anda ingin menutupnya?"</string>
<string name="anr_process" msgid="6513209874880517125">"Proses <xliff:g id="PROCESS">%1$s</xliff:g> tidak menanggapi."\n\n"Anda ingin menutupnya?"</string>
- <string name="force_close" msgid="8346072094521265605">"OK"</string>
+ <string name="force_close" msgid="8346072094521265605">"Oke"</string>
<string name="report" msgid="4060218260984795706">"Laporkan"</string>
<string name="wait" msgid="7147118217226317732">"Tunggu"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"Laman ini tidak menanggapi."\n\n"Apakah Anda ingin menutupnya?"</string>
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ketik PIN yang diminta:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Apl tak dikenal"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Pesan SMS dalam jumlah besar sedang dikirimkan. Sentuh OK untuk melanjutkan, atau Batal untuk menghentikan pengiriman."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Batal"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Kartu SIM dihapus"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Jaringan seluler tidak akan tersedia sampai Anda memulai lagi dengan memasukkan kartu SIM yang valid."</string>
<string name="sim_done_button" msgid="827949989369963775">"Selesai"</string>
@@ -1048,7 +1064,7 @@
<string name="dlg_confirm_kill_storage_users_title" msgid="963039033470478697">"Hidupkan penyimpanan USB"</string>
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Jika Anda menyalakan penyimpanan USB, beberapa apl yang Anda gunakan akan berhenti dan mungkin tidak dapat dibuka hingga penyimpanan USB dimatikan."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"Operasi USB gagal"</string>
- <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
+ <string name="dlg_ok" msgid="7376953167039865701">"Oke"</string>
<string name="usb_mtp_notification_title" msgid="3699913097391550394">"Tersambung sebagai perangkat media"</string>
<string name="usb_ptp_notification_title" msgid="1960817192216064833">"Tersambung sebagai kamera"</string>
<string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Tersambung sebagai pemasang"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangi hari"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tambah tahun"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangi tahun"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"dicentang"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"tidak diperiksa"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"dipilih"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"tidak dipilih"</string>
- <string name="switch_on" msgid="551417728476977311">"nyala"</string>
- <string name="switch_off" msgid="7249798614327155088">"mati"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"ditekan"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"tidak ditekan"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Hapus"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ae52c62..e9e5924 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Consente all\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"cambio velocità del puntatore"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Consente all\'applicazione di modificare la velocità del puntatore del mouse o del trackpad in qualsiasi momento. Non dovrebbe mai essere necessaria per le applicazioni normali."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"modifica del layout tastiera"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Consente all\'applicazione di modificare il layout della tastiera. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"invio segnali Linux alle applicazioni"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Inserisci il PIN richiesto:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Inserisci carattere"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Applicazione sconosciuta"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Invio SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"È in corso l\'invio di numerosi SMS. Tocca \"OK\" per continuare oppure \"Annulla\" per interrompere l\'invio."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Annulla"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Scheda SIM rimossa"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"La rete mobile non sarà disponibile finché non eseguirai il riavvio con una scheda SIM valida inserita."</string>
<string name="sim_done_button" msgid="827949989369963775">"Fine"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tocca per disattivare il debug USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Scegli il metodo di immissione"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configura metodi di immissione"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Tastiera fisica"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidati"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Riduci giorno"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumenta anno"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Riduci anno"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"selezionata"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"non selezionato"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"selezionato"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"non selezionato"</string>
- <string name="switch_on" msgid="551417728476977311">"attivo"</string>
- <string name="switch_off" msgid="7249798614327155088">"disattivo"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"premuto"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"non premuto"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annulla"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Canc"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 613410f..731546c 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"מאפשר ליישום לשנות את הסיבוב של המסך בכל עת. הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"שינוי מהירות המצביע"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"מאפשר ליישום לשנות את המהירות של מצביע העכבר או לוח המגע בכל עת. יישומים רגילים לא אמורים לעולם להזדקק להרשאה זו."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"שנה את פריסת המקלדת"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"מאפשר ליישום לשנות את פריסת המקלדת. הרשאה זו לעולם אינה אמורה להיות נחוצה עבור יישומים רגילים."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"שליחת אותות Linux ליישומים"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"מאפשר ליישום לבקש שהאות שנקלט יישלח לכל התהליכים המתמשכים."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"הגדרת היישום לפעול תמיד"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"הקלד את קוד ה-PIN הנדרש."</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"הוסף תו"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"יישום לא ידוע"</string>
<string name="sms_control_title" msgid="7296612781128917719">"שולח הודעות SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"מתבצעת כעת שליחה של מספר גדול של הודעות SMS. גע באפשרות \'אישור\' כדי להמשיך, או \'ביטול\' כדי לעצור את השליחה."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"אישור"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"ביטול"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"כרטיס ה-SIM הוסר"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"הרשת הסלולרית לא תהיה זמינה עד שתפעיל מחדש לאחר הכנסת כרטיס SIM חוקי."</string>
<string name="sim_done_button" msgid="827949989369963775">"סיום"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"גע כדי להשבית את ניקוי הבאגים בהתקן ה-USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"בחר שיטת הזנה"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"הגדר שיטות קלט"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"מקלדת פיזית"</string>
+ <string name="hardware" msgid="7517821086888990278">"חומרה"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"מועמדים"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"הפחת יום"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"הוסף שנה"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"הפחת שנה"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"מסומן"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"לא מסומן"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"נבחר"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"לא נבחר"</string>
- <string name="switch_on" msgid="551417728476977311">"מופעל"</string>
- <string name="switch_off" msgid="7249798614327155088">"כבוי"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"לחוץ"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"לא לחוץ"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ביטול"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"מחק"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5e5010d..e9d3558 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"いつでも画面の向きを変更することをアプリに許可します。通常のアプリでは不要です。"</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"ポインタの速度の変更"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"マウスまたはトラックパッドのポインタの速度をいつでも変更することをアプリに許可します。通常のアプリでは不要です。"</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"キーボードレイアウトの変更"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"キーボードレイアウトの変更をアプリに許可します。通常のアプリでは不要です。"</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"アプリへのLinuxシグナルの送信"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"受信したシグナルをすべての継続プロセスに送信するようリクエストすることをアプリに許可します。"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"アプリの常時実行"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"必要なPINを入力してください:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"文字を挿入"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"不明なアプリ"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMSメッセージの送信中"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"大量のSMSメッセージを送信しようとしています。[OK]で送信、[キャンセル]で中止します。"</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"キャンセル"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIMカードが取り外されました"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"有効なSIMカードを挿入して再起動するまでは、モバイルネットワークは利用できません。"</string>
<string name="sim_done_button" msgid="827949989369963775">"完了"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"タップしてUSBデバッグを無効にします。"</string>
<string name="select_input_method" msgid="4653387336791222978">"入力方法の選択"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"入力方法をセットアップ"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"物理キーボード"</string>
+ <string name="hardware" msgid="7517821086888990278">"ハードウェア"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"候補"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"1日戻します"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"1年進めます"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"1年戻します"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"ON"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"OFF"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"ON"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"選択されていません"</string>
- <string name="switch_on" msgid="551417728476977311">"ON"</string>
- <string name="switch_off" msgid="7249798614327155088">"OFF"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"ON"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"OFF"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"キャンセル"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"削除"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5556aae..02ab306 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"앱이 언제든지 화면 회전을 변경할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"포인터 속도 변경"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"앱이 언제든지 마우스 또는 트랙패드 포인터의 속도를 변경할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"키보드 레이아웃 변경"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"앱이 키보드 레이아웃을 변경할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"앱에 Linux 시그널 보내기"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"제공된 시그널을 모든 영구 프로세스로 전송하는 것을 앱이 요청할 수 있도록 허용합니다."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"앱이 항상 실행되도록 설정"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"필수 PIN 입력:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"문자 삽입"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"알 수 없는 앱"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"SMS 메시지를 대량 전송 중입니다. 계속하려면 \'확인\'을 터치하고 전송을 중지하려면 \'취소\'를 터치하세요."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"확인"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"취소"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM 카드 제거됨"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"유효한 SIM 카드를 삽입하여 다시 시작할 때까지 모바일 네트워크를 사용할 수 없습니다."</string>
<string name="sim_done_button" msgid="827949989369963775">"완료"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB 디버깅을 사용하지 않으려면 터치하세요."</string>
<string name="select_input_method" msgid="4653387336791222978">"입력 방법 선택"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"입력 방법 설정"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"물리적 키보드"</string>
+ <string name="hardware" msgid="7517821086888990278">"하드웨어"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"가능한 원인"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"\'일\'을 줄입니다."</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"\'연도\'를 늘립니다."</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"\'연도\'를 줄입니다."</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"확인"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"선택 안함"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"선택됨"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"선택 안함"</string>
- <string name="switch_on" msgid="551417728476977311">"켜짐"</string>
- <string name="switch_off" msgid="7249798614327155088">"꺼짐"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"누름"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"누르지 않음"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 키"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"취소"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 키"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9e82e8c..3d389d8 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Leidžiama programai bet kada kaitalioti ekraną. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"keisti žymiklio greitį"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Leidžiama programai keisti pelės ar sensorinio pulto žymiklio greitį. Įprastoms programoms to neturėtų prireikti."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"keisti klaviatūros išdėstymą"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Leidžiama programai pakeisti klaviatūros išdėstymą. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"siųsti „Linux“ signalus programoms"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Leidžiama programai pateikti užklausą, kad teikiamas signalas būtų siunčiamas visiems nuolatiniams procesams."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"nustatyti, kad programa būtų visada vykdoma"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Įveskite reikiamą PIN kodą:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kodas:"</string>
<string name="select_character" msgid="3365550120617701745">"Įterpti simbolį"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nežinoma programa"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS pranešimų siuntimas"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Siunčiama daug SMS pranešimų. Palieskite „Gerai“, jei norite tęsti, arba „Atšaukti“, jei norite sustabdyti siuntimą."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Gerai"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Atšaukti"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM kortelė pašalinta"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilusis tinklas bus nepasiekiamas, kol nepaleisite iš naujo įdėję tinkamą SIM kortelę."</string>
<string name="sim_done_button" msgid="827949989369963775">"Atlikta"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Palieskite, kad neleistumėte USB derinimo."</string>
<string name="select_input_method" msgid="4653387336791222978">"Pasirinkite įvesties metodą"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Nustatyti įvesties metodus"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fizinė klaviatūra"</string>
+ <string name="hardware" msgid="7517821086888990278">"Apar. įr."</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidatai"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Sumažinti dienų skaičių"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Padidinti metų skaičių"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Sumažinti metų skaičių"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"pažymėtas"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nepatikrinta"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"pasirinkta"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nepasirinkta"</string>
- <string name="switch_on" msgid="551417728476977311">"įjungta"</string>
- <string name="switch_off" msgid="7249798614327155088">"išjungta"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"paspausta"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nepaspausta"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atšaukti"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ištrinti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ae451e0..05d0dbe 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Ļauj lietotnei jebkurā laikā mainīt ekrāna pozīciju. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"Rādītāja ātruma mainīšana"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Ļauj lietotnei jebkurā laikā mainīt peles vai skārienpaliktņa rādītāja ātrumu. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"Tastatūras izkārtojuma maiņa"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Ļauj lietotnei mainīt tastatūras izkārtojumu. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"sūtīt Linux signālus lietotnēm"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Ļauj lietotnei pieprasīt, lai piegādātais signāls tiktu sūtīts visiem pastāvīgajiem procesiem."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"likt lietotnei vienmēr darboties"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ierakstiet pieprasīto PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Ievietojiet rakstzīmi"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nezināma lietotne"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Īsziņu sūtīšana"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Tiek sūtīts daudz īsziņu. Pieskarieties Labi, lai turpinātu, vai Atcelt, lai apturētu sūtīšanu."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Labi"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Atcelt"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM karte ir izņemta."</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilais tīkls nebūs pieejams līdz brīdim, kad restartēsiet ierīci ar ievietotu derīgu SIM karti."</string>
<string name="sim_done_button" msgid="827949989369963775">"Gatavs"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Pieskarieties, lai atspējotu USB atkļūdošanu."</string>
<string name="select_input_method" msgid="4653387336791222978">"Ievades metodes izvēle"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Iestatīt ievades metodes"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fiziskā tastatūra"</string>
+ <string name="hardware" msgid="7517821086888990278">"Aparatūra"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidāti"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Norādīt agrāku dienu"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Norādīt vēlāku gadu"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Norādīt agrāku gadu"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"atzīmēta"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nav atzīmēta"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"atlasīta"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nav atlasīta"</string>
- <string name="switch_on" msgid="551417728476977311">"ieslēgts"</string>
- <string name="switch_off" msgid="7249798614327155088">"izslēgts"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"nospiesta"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nav nospiesta"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alternēšanas taustiņš"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atcelt"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Dzēšanas taustiņš"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index ead175b..f0f5630 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Membenarkan apl untuk menukar putaran skrin pada bila-bila masa. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"tukar kelajuan penuding"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Membenarkan apl untuk menukar kelajuan penunjuk tetikus atau pad jejak pada bila-bila masa. Tidak sekali-kali diperlukan untuk apl biasa."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"tukar susun atur papan kekunci"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Membenarkan apl menukar susun atur papan kekunci. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"hantar isyarat Linux kepada apl"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Membenarkan apl meminta isyarat yang dibekalkan dihantar kepada semua proses yang berterusan."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"buatkan apl sentiasa berjalan"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Taipkan PIN yang diperlukan:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Masukkan aksara"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Apl tidak diketahui"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Menghantar mesej SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Sejumlah besar mesej SMS sedang dihantar. Sentuh \"OK\" untuk meneruskan atau \"Batal\" untuk menghentikan penghantaran."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Batal"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Kad SIM dikeluarkan"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Rangkaian mudah alih tidak akan tersedia sehingga anda mula semula dengan kad SIM yang sah dimasukkan."</string>
<string name="sim_done_button" msgid="827949989369963775">"Selesai"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Sentuh untuk melumpuhkan penyahpepijatan USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Pilih kaedah input"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Sediakan kaedah input"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Papan kekunci fizikal"</string>
+ <string name="hardware" msgid="7517821086888990278">"Perkakasan"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangkan hari"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tingkatkan tahun"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangkan tahun"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"ditandakan"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"tidak ditandakan"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"dipilih"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"tidak dipilih"</string>
- <string name="switch_on" msgid="551417728476977311">"hidup"</string>
- <string name="switch_off" msgid="7249798614327155088">"mati"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"ditekan."</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"tidak ditekan"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Padam"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index bbd846f..6609d29 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv inn påkrevd PIN-kode:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Sett inn tegn"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ukjent app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Sender SMS-meldinger"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Et stort antall SMS-meldinger sendes. Trykk på OK for å fortsette, eller trykk på Avbryt for å avbryte sendingen."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kort er fjernet"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Det mobile nettverket forblir utilgjengelig inntil du starter på nytt med et gyldig SIM-kort."</string>
<string name="sim_done_button" msgid="827949989369963775">"Fullført"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduser dager"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Øk år"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduser år"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"valgt"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"ikke valgt"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"valgt"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ikke valgt"</string>
- <string name="switch_on" msgid="551417728476977311">"på"</string>
- <string name="switch_off" msgid="7249798614327155088">"av"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"trykket"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ikke trykket"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slett"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 8440d62..1c8809a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Hiermee kan de app de rotatie van het scherm op elk moment wijzigen. Nooit vereist voor normale apps."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"aanwijzersnelheid wijzigen"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Hiermee kan de app de snelheid van de muis- of trackpadaanwijzer op elk moment wijzigen. Nooit vereist voor normale apps."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"toetsenbordindeling wijzigen"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Hiermee kan de app de toetsenbordindeling wijzigen. Nooit vereist voor normale apps."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Linux-signalen verzenden naar apps"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Hiermee kan de app ervoor zorgen dat het geleverde signaal wordt verzonden naar alle persistente processen."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"app altijd laten uitvoeren"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer de gewenste pincode in:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pincode"</string>
<string name="select_character" msgid="3365550120617701745">"Teken invoegen"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Onbekende app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS-berichten verzenden"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Er wordt een groot aantal sms\'jes verzonden. Raak \'OK\' aan om door te gaan of \'Annuleren\' om de verzending te stoppen."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Annuleren"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Simkaart verwijderd"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Het mobiele netwerk is niet beschikbaar totdat u het apparaat opnieuw start met een geldige simkaart."</string>
<string name="sim_done_button" msgid="827949989369963775">"Gereed"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Raak deze optie aan om USB-foutopsporing uit te schakelen."</string>
<string name="select_input_method" msgid="4653387336791222978">"Invoermethode selecteren"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Invoermethoden instellen"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fysiek toetsenbord"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaten"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Lagere waarde voor dag"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Hogere waarde voor jaar"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Lagere waarde voor jaar"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"aangevinkt"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"niet aangevinkt"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"geselecteerd"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"niet geselecteerd"</string>
- <string name="switch_on" msgid="551417728476977311">"aan"</string>
- <string name="switch_off" msgid="7249798614327155088">"uit"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"ingedrukt"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"niet ingedrukt"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuleren"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ee1e6a3..83b7aba 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Pozwala aplikacji na zmianę obrotu ekranu w dowolnym momencie. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"zmiana szybkości wskaźnika"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Pozwala aplikacji zmienić szybkość wskaźnika myszy lub touchpada w dowolnym momencie. Nieprzeznaczone dla zwykłych aplikacji."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"zmiana układu klawiatury"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Zezwala aplikacji na zmianę układu klawiatury. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"wysyłanie sygnałów systemu Linux do aplikacji"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Pozwala aplikacji na żądanie, aby dostarczony sygnał został wysłany do wszystkich trwałych procesów."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"sprawianie, że aplikacja jest cały czas uruchomiona"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Wpisz wymagany kod PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Kod PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Wstaw znak"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nieznana aplikacja"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Wysyłanie wiadomości SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Wysyłana jest duża liczba wiadomości SMS. Dotknij OK, aby kontynuować, lub Anuluj, aby zatrzymać wysyłanie."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Anuluj"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Karta SIM wyjęta"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Sieć komórkowa będzie niedostępna do chwili ponownego uruchomienia urządzenia z użyciem ważnej karty SIM."</string>
<string name="sim_done_button" msgid="827949989369963775">"Gotowe"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotknij, aby wyłączyć debugowanie USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Wybierz metodę wprowadzania"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Konfiguruj metody wprowadzania"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Klawiatura fizyczna"</string>
+ <string name="hardware" msgid="7517821086888990278">"Sprzęt"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCĆDEĘFGHIJKLŁMNŃOÓPQRSŚTUVWXYZŹŻ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandydaci"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmień dzień na wcześniejszy"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Zmień rok na późniejszy"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmień rok na wcześniejszy"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"zaznaczono"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nie zaznaczono"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"wybrano"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie wybrano"</string>
- <string name="switch_on" msgid="551417728476977311">"włączono"</string>
- <string name="switch_off" msgid="7249798614327155088">"wyłączono"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"naciśnięto"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nie naciśnięto"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anuluj"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 393ba17..8f2f78f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permite que a aplicação altere a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"alterar a veloc. do ponteiro"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permite à aplicação mudar em qualquer altura a velocidade do ponteiro do rato ou do trackpad. Nunca deverá ser necessário para aplicações normais."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"alterar o esquema de teclado"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permite que a aplicação altere o esquema de teclado. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"enviar sinais Linux para aplicações"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Permite à aplicação pedir que o sinal fornecido seja enviado a todos os processos persistentes."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"fazer com que a aplicação seja sempre executada"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduza o PIN solicitado:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Introduzir carácter"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicação desconhecida"</string>
<string name="sms_control_title" msgid="7296612781128917719">"A enviar mensagens SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Está a ser enviado um grande número de mensagens SMS. Toque em “OK” para continuar ou “Cancelar” para parar o envio."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Cartão SIM removido"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"A rede de telemóvel estará indisponível até que reinicie o aparelho com um cartão SIM válido inserido."</string>
<string name="sim_done_button" msgid="827949989369963775">"Concluído"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toque para desativar a depuração USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Escolher o método de entrada"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configurar métodos de introdução"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclado físico"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"marcado"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"desmarcado"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"selecionado"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"não selecionado"</string>
- <string name="switch_on" msgid="551417728476977311">"ativado"</string>
- <string name="switch_off" msgid="7249798614327155088">"desativado"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"premido"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"não premido"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bb145f7..4d770b5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Permite que o aplicativo gire a tela a qualquer momento. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"alterar velocidade do ponteiro"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Permite que o aplicativo altere a velocidade do cursos do mouse ou trackpad a qualquer momento. Nunca deve ser necessário para aplicativos normais."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"alterar layout do teclado"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Permite que o aplicativo altere o layout do teclado. Não será necessário para aplicativos normais."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"enviar sinais para aplicativos Linux"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Permite que o aplicativo solicite o envio do sinal fornecido a todos os processos persistentes."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"sempre executar o aplicativo"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Digite o PIN obrigatório:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Inserir caractere"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicativo desconhecido"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Enviando mensagens SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Muitas mensagens SMS estão sendo enviadas. Selecione \"OK\" para continuar ou \"Cancelar\" para interromper o envio."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Cartão SIM removido"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"A rede móvel ficará indisponível até que você reinicie com um cartão SIM válido inserido."</string>
<string name="sim_done_button" msgid="827949989369963775">"Concluído"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toque para desativar a depuração do USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Selecione o método de entrada"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Configurar métodos de entrada"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclado físico"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"verificado"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"não selecionado"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"selecionado"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"Não selecionado"</string>
- <string name="switch_on" msgid="551417728476977311">"ativado"</string>
- <string name="switch_off" msgid="7249798614327155088">"desativado"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"pressionado"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"não pressionado"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Excluir"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 8b0718f..6f066de 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1489,13 +1489,27 @@
<!-- no translation found for wifi_p2p_show_pin_message (8530563323880921094) -->
<skip />
<string name="select_character" msgid="3365550120617701745">"Inserir in caracter"</string>
- <!-- no translation found for sms_control_default_app_name (3058577482636640465) -->
- <skip />
<string name="sms_control_title" msgid="7296612781128917719">"Trametter messadis SMS"</string>
- <!-- no translation found for sms_control_message (4073755190243093924) -->
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
<skip />
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Interrumper"</string>
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<!-- no translation found for sim_removed_title (6227712319223226185) -->
<skip />
<!-- no translation found for sim_removed_message (2333164559970958645) -->
@@ -1788,22 +1802,6 @@
<skip />
<!-- no translation found for date_picker_decrement_year_button (4482021813491121717) -->
<skip />
- <!-- no translation found for checkbox_checked (7222044992652711167) -->
- <skip />
- <!-- no translation found for checkbox_not_checked (5174639551134444056) -->
- <skip />
- <!-- no translation found for radiobutton_selected (8603599808486581511) -->
- <skip />
- <!-- no translation found for radiobutton_not_selected (2908760184307722393) -->
- <skip />
- <!-- no translation found for switch_on (551417728476977311) -->
- <skip />
- <!-- no translation found for switch_off (7249798614327155088) -->
- <skip />
- <!-- no translation found for togglebutton_pressed (4180411746647422233) -->
- <skip />
- <!-- no translation found for togglebutton_not_pressed (4495147725636134425) -->
- <skip />
<!-- no translation found for keyboardview_keycode_alt (4856868820040051939) -->
<skip />
<!-- no translation found for keyboardview_keycode_cancel (1203984017245783244) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d93d8d1..3772861 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduceţi codul PIN necesar:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Cod PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Introduceţi caracterul"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicaţie necunoscută"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Se trimit mesaje SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"În acest moment se trimit multe mesaje SMS. Atingeţi „OK” pentru a continua sau „Anulaţi” pentru a opri trimiterea."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Anulaţi"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Card SIM eliminat"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Reţeaua mobilă va fi indisponibilă până când reporniţi cu o cartelă SIM validă introdusă."</string>
<string name="sim_done_button" msgid="827949989369963775">"Terminat"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"bifată"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nebifată"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"selectat"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"neselectat"</string>
- <string name="switch_on" msgid="551417728476977311">"activat"</string>
- <string name="switch_off" msgid="7249798614327155088">"dezactivat"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"apăsat"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"neapăsat"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulaţi"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ştergeţi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 2743763..b967620 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Приложение сможет менять ориентацию экрана. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"изменять скорость указателя"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Приложение сможет в любой момент изменить скорость движения указателя мыши или сенсорной панели. Это разрешение не используется обычными приложениями."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"менять раскладку автоматически"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Разрешить автоматическую смену раскладки клавиатуры (используется лишь в некоторых приложениях)."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"отправка сигналов Linux приложениям"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Приложение сможет запрашивать передачу полученного сигнала всем постоянным процессам."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"постоянная работа приложения"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введите PIN-код:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
<string name="select_character" msgid="3365550120617701745">"Введите символ"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Неизвестное приложение"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Отправка SMS-сообщений"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Отправляется большое количество SMS-сообщений. Нажмите \"ОК\", чтобы продолжить отправку, или \"Отмена\", чтобы прекратить ее."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"ОК"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Отмена"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-карта удалена"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Пока вы не вставите действующую SIM-карту, мобильная сеть будет недоступна."</string>
<string name="sim_done_button" msgid="827949989369963775">"Готово"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Нажмите, чтобы отключить отладку по USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Выберите способ ввода"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Настройка способов ввода"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Физическая клавиатура"</string>
+ <string name="hardware" msgid="7517821086888990278">"Аппаратура"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"варианты"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"На год вперед"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На год назад"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"установлено"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"не установлено"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"выбрано"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"не выбрано"</string>
- <string name="switch_on" msgid="551417728476977311">"Включено"</string>
- <string name="switch_off" msgid="7249798614327155088">"Выкл."</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"нажато"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не нажато"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Клавиша ALT"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отмена"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Клавиша удаления"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 49d04c3..1bf7a17 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Umožňuje aplikácii kedykoľvek zmeniť otáčanie obrazovky. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"zmena rýchlosti ukazovateľa"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Umožňuje aplikácii kedykoľvek zmeniť rýchlosť kurzora myši alebo touchpadu. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"zmeniť rozloženie klávesnice"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Umožňuje aplikácii zmeniť rozloženie klávesnice. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"odoslať aplikáciám signály systému Linux"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Umožňuje aplikácii vyžiadať odoslanie poskytnutého signálu všetkým trvalým procesom."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"nastaviť, aby bola aplikácia neustále spustená"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadajte požadovaný kód PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Vkladanie znakov"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznáma aplikácia"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Odosielanie správ SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Odosiela sa veľký počet správ SMS. Ak chcete pokračovať, dotknite sa tlačidla OK. Ak chcete odosielanie ukončiť, dotknite sa tlačidla Zrušiť."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Zrušiť"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Karta SIM bola odobraná"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilná sieť nebude k dispozícii, kým nevložíte platnú kartu SIM a zariadenie nereštartujete."</string>
<string name="sim_done_button" msgid="827949989369963775">"Hotovo"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotknutím zakážete ladenie USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Zvoliť metódu vstupu"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Nastavenie metód vstupu"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fyzická klávesnica"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardvér"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁÄBCČDĎDZDŽEÉFGHCHIÍJKLĽMNŇOÓÔPRŔSŠTŤUÚVWXYÝZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidáti"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrať deň"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Pridať rok"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrať rok"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"začiarknuté"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"nezačiarknuté"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"vybratý"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"nie je vybraté"</string>
- <string name="switch_on" msgid="551417728476977311">"zapnuté"</string>
- <string name="switch_off" msgid="7249798614327155088">"vypnuté"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"stlačené"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"nestlačené"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušiť"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Odstrániť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 55f3593..152d1c1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Programu omogoča, da kadar koli zasuka zaslon. Ne uporabljajte za navadne programe."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"spreminjanje hitrosti kazalca"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Programu omogoča spreminjanje hitrosti kazalca miške ali sledilne ploščice. Tega ni treba nikoli uporabiti za navadne programe."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"spreminjanje postavitve tipkovnice"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Aplikaciji omogoča, da kadar koli spremeni postavitev tipkovnice. Običajne aplikacije tega ne potrebujejo."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"pošiljanje signalov Linuxa programom"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Programu omogoča, da zahteva, da je posredovani signal poslan vsem trajnim procesom."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"neprekinjeno izvajanje programov"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Vnesite zahtevano kodo PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Vstavljanje znaka"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznan program"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Pošiljanje sporočil SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"V pošiljanju je veliko sporočil SMS. Če želite nadaljevati, izberite »V redu«. Če želite pošiljanje ustaviti, izberite »Prekliči«."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"V redu"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Prekliči"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Kartica SIM odstranjena"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mobilno omrežje ne bo na voljo, dokler naprave vnovič ne zaženete z veljavno kartico SIM."</string>
<string name="sim_done_button" msgid="827949989369963775">"Dokončano"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotaknite se, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Izberite način vnosa"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Nastavi načine vnosa"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fizična tipkovnica"</string>
+ <string name="hardware" msgid="7517821086888990278">"Strojna oprema"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmanjšanje vrednosti za dan"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povečanje vrednosti za leto"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmanjšanje vrednosti za leto"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"potrjeno"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"ni odkljukano"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"izbrano"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ni izbrano"</string>
- <string name="switch_on" msgid="551417728476977311">"vklopljeno"</string>
- <string name="switch_off" msgid="7249798614327155088">"izklopljeno"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"vklopljen"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"izklopljen"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Tipka Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Prekliči"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tipka Delete"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index de03bec..9e63b31 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Унесите потребни PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Уметање знака"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Непозната апликација"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Слање SMS порука"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Шаље се велики број SMS порука. Додирните Потврди да бисте наставили или Откажи да бисте зауставили слање."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Потврди"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Откажи"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM картица је уклоњена"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Мобилна мрежа неће бити доступна док не покренете систем поново уз уметање важеће SIM картице."</string>
<string name="sim_done_button" msgid="827949989369963775">"Готово"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Смањивање дана"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Повећавање године"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Смањивање године"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"изабрано"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"није потврђено"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"изабрано"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"није изабрано"</string>
- <string name="switch_on" msgid="551417728476977311">"укључено"</string>
- <string name="switch_off" msgid="7249798614327155088">"искључено"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"притиснуто"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"није притиснуто"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Откажи"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Избриши"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 7448ff8..3db9ec7 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Gör att appen när som helst kan ändra skärmläget. Behövs inte för vanliga appar."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"ändra markörens hastighet"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Tillåter att appen när som helst ändrar hastigheten för musens eller styrplattans markör. Ska inte behövas för vanliga appar."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"ändra tangentbordslayout"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Tillåter att appen ändrar tangentbordets layout. Ska inte behövas för vanliga appar."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"skicka Linux-signaler till appar"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Tillåter att appen begär att den angivna signalen skickas till alla beständiga processer."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
<string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Okänd app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Flera SMS skickas. Tryck på OK om du vill fortsätta eller på Avbryt om du vill avsluta sändningen."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-kortet togs bort"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Det mobila nätverket kommer inte att vara tillgängligt förrän du startar om med ett giltigt SIM-kort."</string>
<string name="sim_done_button" msgid="827949989369963775">"Klar"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tryck om du vill inaktivera USB-felsökning."</string>
<string name="select_input_method" msgid="4653387336791222978">"Välj inmatningsmetod"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Konfigurera inmatningsmetoder"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fysiskt tangentbord"</string>
+ <string name="hardware" msgid="7517821086888990278">"Maskinvara"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Minska dagar"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Öka år"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Minska år"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"markerat"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"inte markerat"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"markerade"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ej vald"</string>
- <string name="switch_on" msgid="551417728476977311">"på"</string>
- <string name="switch_off" msgid="7249798614327155088">"av"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"intryckt"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"inte intryckt"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ab6c82f..20bc2c6 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Inaruhusu programu kubadilisha mzunguko wa skrini wakati wowote. Kamwe hazihitajiki kwa programu za kawaida."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"Badilisha kasi ya pointa"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Inaruhusu programu kubadilisha kasi ya kielekezi cha kipanya au pedi ya kufuatilia wakati wowote. Kamwe haitahitajika kwa programu za kawaida."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"badilisha mpangilio wa kibodi"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Inaruhusu programu kubadilisha mpangilio wa kibodi. Haipaswi kamwe kuhitajika kwa rogramu za kawaida."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Tuma ishara za Linux kwa programu"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Inaruhusu programu kuomba ishara iliyotolewa kutumwa kwa michakato inyoendelea."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"Fanya programu kuendeshwa kila mara"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Charaza PIN inayohitajika:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Programu isiyojulikana"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Idadi kubwa ya jumbe za SMS inatumwa. Gusa SAWA kuendelea, au Ghairi kuwacha kutuma."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Sawa"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Ghairi"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Kadi ya SIM imeondolewa"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"mtandao wa simu hutapatika hadi uanzishe upya na SIM kadi halali iliyoingizwa."</string>
<string name="sim_done_button" msgid="827949989369963775">"Kwisha"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Gusa ili kulemaza utatuaji wa USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Chagua njia ya ingizo"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Weka mbinu za ingizo"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Kibodi halisi"</string>
+ <string name="hardware" msgid="7517821086888990278">"Maunzi"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"wagombeaji"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Punguza siku"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Ongeza mwaka"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Punguza mwaka"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"imeangaliwa"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"haijakaguliwa"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"Iliyochaguliwa"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"Haijachaguliwa"</string>
- <string name="switch_on" msgid="551417728476977311">"Washa"</string>
- <string name="switch_off" msgid="7249798614327155088">"zima"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"iliyobonyezwa"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"Haijabonyezwa"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ghairi"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Futa"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7007522..0884fb7 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"พิมพ์ PIN ที่ต้องการ:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"ใส่อักขระ"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"แอปพลิเคชันที่ไม่รู้จัก"</string>
<string name="sms_control_title" msgid="7296612781128917719">"กำลังส่งข้อความ SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"กำลังส่งข้อความ SMS จำนวนมาก แตะ \"ตกลง\" เพื่อทำงานต่อหรือ \"ยกเลิก\" เพื่อหยุดส่ง"</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"ตกลง"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"ยกเลิก"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"นำซิมการ์ดออกแล้ว"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"เครือข่ายมือถือจะไม่สามารถใช้งานได้จนกว่าคุณจะรีสตาร์ทโดยใส่ซิมการ์ดที่ถูกต้องแล้ว"</string>
<string name="sim_done_button" msgid="827949989369963775">"เสร็จสิ้น"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ลดวัน"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"เพิ่มปี"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ลดปี"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"เลือกไว้"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"ไม่ได้ตรวจสอบ"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"เลือกแล้ว"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"ไม่ได้เลือก"</string>
- <string name="switch_on" msgid="551417728476977311">"เปิด"</string>
- <string name="switch_off" msgid="7249798614327155088">"ปิด"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"กดแล้ว"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"ไม่ได้กด"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ยกเลิก"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ลบ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 31ad6c8..26f7d5e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Pinapayagan ang app na baguhin ang pag-ikot ng screen anumang oras. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"baguhin ang bilis ng pointer"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Pinapayagan ang app na baguhin ang bilis ng mouse o trackpad pointer anumang oras. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"baguhin ang layout ng keyboard"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Pinapayagan ang app na baguhin ang layout ng keyboard. Hindi kailanman dapat na kailanganin para sa mga normal na app."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"magpadala ng mga signal ng Linux sa apps"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Pinapayagan ang app na hilinging maipadala ang ibinigay na signal sa lahat ng nagpapatuloy na proseso."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"palaging patakbuhin ang app"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"I-type ang kinakailangang PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Magpasok ng character"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Hindi kilalang app"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Nagpapadala ng mga SMS na mensahe"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Ipinapadala ang malaking bilang ng mga mensaheng SMS. Pindutin ang OK upang magpatuloy, o Kanselahin upang ihinto ang pagpapadala."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Kanselahin"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Naalis ang SIM card"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Hindi magiging available ang mobile network hanggang mag-restart ka gamit ang isang may-bisang SIM card"</string>
<string name="sim_done_button" msgid="827949989369963775">"Tapos na"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Pindutin upang huwag paganahin ang pag-debug ng USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Pumili ng pamamaraan ng pag-input"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"I-set up paraan ng pag-input"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Aktwal na keyboard"</string>
+ <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"mga kandidato"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Bawasan ang araw"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Dagdagdan ang taon"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Bawasan ang taon"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"nilagyan ng check"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"hindi nilagyan ng check"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"pinili"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"Hindi pinili"</string>
- <string name="switch_on" msgid="551417728476977311">"naka-on"</string>
- <string name="switch_off" msgid="7249798614327155088">"naka-off"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"pinindot"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"hindi pinindot"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselahin"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tanggalin"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 959d8bd..8abf38d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Uygulamaya, istediği zaman ekran dönüşünü değiştirme izni verir. Normal uygulamalar için gerekli değildir."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"işaretçi hızını değiştir"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Uygulamaya, istediği zaman fare veya izleme yüzeyi işaretçi hızını değiştirme izni verir. Normal uygulamalar için gerekli değildir."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"klavye düzenini değiştir"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Uygulamaya klavye düzenini değiştirme izni verir. Normal uygulamalar için hiçbir zaman gerekmez."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"uygulamalara Linux sinyalleri gönder"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Uygulamaya, sağlanan sinyalin tüm kalıcı işlemlere gönderilmesini isteme izni verir."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"uygulamayı her zaman çalıştır"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Gerekli PIN\'i yazın:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Bilinmeyen uygulama"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Çok sayıda SMS mesajı gönderiliyor. Devam etmek için Tamam\'a, göndermeyi durdurmak için İptal\'e dokunun."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"Tamam"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"İptal"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM kart çıkarıldı"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Geçerli bir SIM kart yerleştirilmiş olarak yeniden başlatana kadar mobil ağ kullanılamayacak."</string>
<string name="sim_done_button" msgid="827949989369963775">"Tamamlandı"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB hata ayıklama özelliğini devre dışı bırakmak için dokunun."</string>
<string name="select_input_method" msgid="4653387336791222978">"Giriş yöntemini seçin"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Giriş yöntemlerini ayarla"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Fiziksel klavye"</string>
+ <string name="hardware" msgid="7517821086888990278">"Donanım"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"adaylar"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Günü azalt"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Yılı artır"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yılı azalt"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"işaretli"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"işaretlenmedi"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"seçili"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"seçili değil"</string>
- <string name="switch_on" msgid="551417728476977311">"açık"</string>
- <string name="switch_off" msgid="7249798614327155088">"kapalı"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"basıldı"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"basılmadı"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"İptal"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Sil"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f9d372e..5504176 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введіть потрібний PIN-код:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
<string name="select_character" msgid="3365550120617701745">"Вставл-ня символу"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Невідома програма"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Надсил. SMS повідомлень"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Надсилається велика кількість SMS повідомлень. Торкніться \"OK\", щоб продовжити, або \"Скасувати\", щоб припинити надсилання."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Скасувати"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM-карту вилучено"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Мобільна мережа буде недоступна, поки ви не здійсните перезапуск, вставивши дійсну SIM-карту."</string>
<string name="sim_done_button" msgid="827949989369963775">"Готово"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"На рік уперед"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На рік назад"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"перевірено"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"не перевірено"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"вибрано"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"не вибрано"</string>
- <string name="switch_on" msgid="551417728476977311">"увімк."</string>
- <string name="switch_off" msgid="7249798614327155088">"вимкн."</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"натиснуто"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"не натиснуто"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Скасувати"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index dde1ad4..96e97f6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Cho phép ứng dụng thay đổi độ xoay màn hình bất cứ lúc nào. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"thay đổi tốc độ con trỏ"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Cho phép ứng dụng thay đổi tốc độ của chuột hoặc con trỏ trên ô di chuột bất kỳ lúc nào. Không cần thiết cho các ứng dụng thông thường."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"thay đổi bố cục bàn phím"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Cho phép các ứng dụng thay đổi bố cục bàn phím. Không bao giờ cần thiết cho các ứng dụng bình thường."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"gửi tín hiệu Linux đến ứng dụng"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"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="8841113627955563938">"đặt ứng dụng luôn chạy"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Nhập PIN bắt buộc:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Mã PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Chèn ký tự"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ứng dụng không xác định"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Đang gửi tin nhắn SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Một lượng lớn các tin nhắn SMS đang được gửi. Chạm OK để tiếp tục hoặc Hủy để ngừng gửi."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Hủy"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"Đã xóa thẻ SIM"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Mạng di động sẽ không khả dụng cho đến khi bạn khởi động lại với thẻ SIM hợp lệ được lắp."</string>
<string name="sim_done_button" msgid="827949989369963775">"Xong"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Chạm để vô hiệu hóa gỡ lỗi USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Chọn phương thức nhập"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Thiết lập phương thức nhập"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Bàn phím thực"</string>
+ <string name="hardware" msgid="7517821086888990278">"Phần cứng"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ứng viên"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Giảm ngày"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tăng năm"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Giảm năm"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"đã kiểm tra"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"chưa chọn"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"đã chọn"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"chưa được chọn"</string>
- <string name="switch_on" msgid="551417728476977311">"bật"</string>
- <string name="switch_off" msgid="7249798614327155088">"tắt"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"đã bấm"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"chưa được bấm"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Hủy"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Xóa"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0c338bb..08a15e4 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"允许应用随时更改屏幕的旋转状态。普通应用绝不需要此权限。"</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"更改指针速度"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"允许应用随时更改鼠标或触控板指针速度。普通应用绝不需要此权限。"</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"更改键盘布局"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"允许应用更改键盘布局。普通应用绝不需要此权限。"</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"向应用发送 Linux 信号"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"允许应用请求将提供的信号发送给所有持续的进程。"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"让应用始终运行"</string>
@@ -1009,11 +1007,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"插入字符"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"未知应用"</string>
<string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"正在发送大量短信。触摸“确定”继续,或触摸“取消”停止发送。"</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"确定"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"取消"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"已移除 SIM 卡"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"移动网络不可用。请插入有效的 SIM 卡并重新启动。"</string>
<string name="sim_done_button" msgid="827949989369963775">"完成"</string>
@@ -1063,10 +1077,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"触摸以停用 USB 调试。"</string>
<string name="select_input_method" msgid="4653387336791222978">"选择输入法"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"设置输入法"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"物理键盘"</string>
+ <string name="hardware" msgid="7517821086888990278">"硬件"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"候选"</u></string>
@@ -1193,14 +1205,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"减小日期值"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"增大年份值"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"减小年份值"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"已选中"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"未选中"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"已选择"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"未选择"</string>
- <string name="switch_on" msgid="551417728476977311">"已打开"</string>
- <string name="switch_off" msgid="7249798614327155088">"已关闭"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"已按下"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"未按下"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index c36b1b5..0468534 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1009,11 +1009,27 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"請輸入必要的 PIN:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"插入字元"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"不明的應用程式"</string>
<string name="sms_control_title" msgid="7296612781128917719">"傳送 SMS 簡訊"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"即將傳送大量的 SMS 簡訊。輕觸 [確定] 可繼續傳送,或輕觸 [取消] 停止傳送。"</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"確定"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"取消"</string>
+ <!-- no translation found for sms_control_message (3867899169651496433) -->
+ <skip />
+ <!-- no translation found for sms_control_yes (3663725993855816807) -->
+ <skip />
+ <!-- no translation found for sms_control_no (625438561395534982) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_title (1666863092640877318) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_title (3811263856304367838) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_message (5616409294907295407) -->
+ <skip />
+ <!-- no translation found for sms_premium_short_code_confirm_message (6214083016284738667) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_allow (8957573662645722940) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_deny (6374609298084435887) -->
+ <skip />
+ <!-- no translation found for sms_short_code_confirm_report (2588793956061677070) -->
+ <skip />
<string name="sim_removed_title" msgid="6227712319223226185">"SIM 卡已移除"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"您必須先插入有效的 SIM 卡再重新啟動手機,才能使用行動網路。"</string>
<string name="sim_done_button" msgid="827949989369963775">"完成"</string>
@@ -1193,14 +1209,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"減少日數"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"增加年數"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"減少年數"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"已勾選"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"尚未勾選"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"已選取"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"未選取"</string>
- <string name="switch_on" msgid="551417728476977311">"開啟"</string>
- <string name="switch_off" msgid="7249798614327155088">"關閉"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"已按下"</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"未按下"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 鍵"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 鍵"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3b3b1e1..c910d4a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -286,10 +286,8 @@
<string name="permdesc_setOrientation" msgid="3046126619316671476">"Ivumela insiza ukuthi iguqule ukujikeleza kweskrini nganoma isiphi isikhathi. Akudingakeli izinsiza ezejwayelekile."</string>
<string name="permlab_setPointerSpeed" msgid="9175371613322562934">"guqula isivinini sesikhombi"</string>
<string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"Ivumela insiza ukuthi iguqule ijubane legundane noma lendawo yokukhomba ngomunwe. Akufanele kudingakele izinsiza ezijwayelekile."</string>
- <!-- no translation found for permlab_setKeyboardLayout (4778731703600909340) -->
- <skip />
- <!-- no translation found for permdesc_setKeyboardLayout (8480016771134175879) -->
- <skip />
+ <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"shintsha isendlalelo sekhibhodi"</string>
+ <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"Ivumela uhlelo lokusebenza ukushintsha isendlalelo sekhibhodi. Kufanele ingadingi izinhlelo zokusebenzia ezivamile."</string>
<string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"Thumela imifanekiso ye-Linu ezinsizeni"</string>
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Ivumela insiza ukuthi icele ukuthi isiginali ethunyelwe idluliselwe kuzo zonke izinqubeko ezisalelayo."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"yenza insiza ukuthi ihlale isebenza"</string>
@@ -1009,11 +1007,17 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Faka i-PIN edingekayo:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
<string name="select_character" msgid="3365550120617701745">"Faka uhlamvu"</string>
- <string name="sms_control_default_app_name" msgid="3058577482636640465">"insiza engaziwa"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Ithumela imiyalezo ye-SMS"</string>
- <string name="sms_control_message" msgid="4073755190243093924">"Inani eliphezulu lwama-SMS liyathunyelwa. Khetha \"KULUNGILE\" ukuqhubeka, noma \"Khansela\" ukumisa ukuthumela."</string>
- <string name="sms_control_yes" msgid="2532062172402615953">"KULUNGILE"</string>
- <string name="sms_control_no" msgid="1715320703137199869">"Khansela"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"I-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ithumela inombolo enkulu yemilayezo ye-SMS. Ufuna ukuvumela lolu hlelo lokusebenza ukuqhubeka ukuthumela imilayezo?"</string>
+ <string name="sms_control_yes" msgid="3663725993855816807">"Vumela"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Nqaba"</string>
+ <string name="sms_short_code_confirm_title" msgid="1666863092640877318">"Thumela ikhodi efushane?"</string>
+ <string name="sms_premium_short_code_confirm_title" msgid="3811263856304367838">"Ukuthumela i-SMS ye-premium?"</string>
+ <string name="sms_short_code_confirm_message" msgid="5616409294907295407">"I-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ithanda ukuthumela umlayezo wombhalo ku-<b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, okubonakala sengathi ikhodi ye-SMS efushane.<p>Ukuthumela umlayezo wombhalo kungabangela i-akhawunti yeselula yakho ukuthi ikhokheliswe amasevisi e-premium.<p>Ufuna ukuvumela uhlelo lokusebenza ukuthumela umlayezo?"</string>
+ <string name="sms_premium_short_code_confirm_message" msgid="6214083016284738667">"I-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ithanda ukuthumela umlayezo wombhalo ku-<b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>, okuyikhodi efushane ye-SMS ye-premium.<p><b>Ukuthumela umlayezo kule ndawo kuzobangela i-akhawunti yeselula yakho ukuthi ikhokheliswe amasevisi e-premium.</b><p>Ufuna ukuvumela lolu hlelo lokusebenza ukuthumela umlayezo?"</string>
+ <string name="sms_short_code_confirm_allow" msgid="8957573662645722940">"Thumela umlayezo?"</string>
+ <string name="sms_short_code_confirm_deny" msgid="6374609298084435887">"Ungathumeli"</string>
+ <string name="sms_short_code_confirm_report" msgid="2588793956061677070">"Bika uhlelo lokusebenza olungalungile"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"Ikhadi le-SIM likhishiwe"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"Inethiwekhi yeselula ngeke itholakale kuwena kuze kube uqala kabusha ufake ikhadi le-SIM elifanele."</string>
<string name="sim_done_button" msgid="827949989369963775">"Kwenziwe"</string>
@@ -1063,10 +1067,8 @@
<string name="adb_active_notification_message" msgid="1016654627626476142">"Thinta ukwenza ukuthi ukudibhaga kwe-USB kungasebenzi."</string>
<string name="select_input_method" msgid="4653387336791222978">"Khetha indlela yokufaka"</string>
<string name="configure_input_methods" msgid="9091652157722495116">"Izilungiselelo zezindlela zokufakwayo"</string>
- <!-- no translation found for use_physical_keyboard (6203112478095117625) -->
- <skip />
- <!-- no translation found for hardware (7517821086888990278) -->
- <skip />
+ <string name="use_physical_keyboard" msgid="6203112478095117625">"Ukwakheka kwekhibhodi"</string>
+ <string name="hardware" msgid="7517821086888990278">"I-Hardware"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"abahlanganyeli"</u></string>
@@ -1193,14 +1195,6 @@
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Yehlisa usuku"</string>
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"Khulisa unyaka"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yehlisa unyaka"</string>
- <string name="checkbox_checked" msgid="7222044992652711167">"kuhloliwe"</string>
- <string name="checkbox_not_checked" msgid="5174639551134444056">"akuhloliwe"</string>
- <string name="radiobutton_selected" msgid="8603599808486581511">"Okukhethiwe"</string>
- <string name="radiobutton_not_selected" msgid="2908760184307722393">"akukhethiwe"</string>
- <string name="switch_on" msgid="551417728476977311">"vuliwe"</string>
- <string name="switch_off" msgid="7249798614327155088">"valiwe"</string>
- <string name="togglebutton_pressed" msgid="4180411746647422233">"kucindezelwe."</string>
- <string name="togglebutton_not_pressed" msgid="4495147725636134425">"akucindezelwe."</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"i-ALT"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Khansela"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Susa"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index aabe407..de24d10 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -835,6 +835,10 @@
<!-- Reference to the Pointer style -->
<attr name="pointerStyle" format="reference" />
+
+ <!-- The drawable for accessibility focused views. -->
+ <attr name="accessibilityFocusedDrawable" format="reference" />
+
</declare-styleable>
<!-- **************************************************************** -->
@@ -2123,6 +2127,20 @@
layoutDirection is LTR, and ALIGN_LEFT otherwise -->
<enum name="viewEnd" value="6" />
</attr>
+
+ <!-- Controls how this View is important for accessibility which is if it fires
+ accessibility events and if it is reported to accessibility services that
+ query the screen. Note: While not recommended, an accessibility service may
+ decide to ignore this attribute and operate on all views in the view tree. -->
+ <attr name="importantForAccessibility" format="integer">
+ <!-- The system determines whether the view is important for accessibility (recommended). -->
+ <enum name="auto" value="0" />
+ <!-- The view is important for accessibility. -->
+ <enum name="yes" value="1" />
+ <!-- The view is not important for accessibility. -->
+ <enum name="no" value="2" />
+ </attr>
+
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -2445,6 +2463,8 @@
<attr name="accessibilityFlags">
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT} -->
<flag name="flagDefault" value="0x00000001" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#INCLUDE_NOT_IMPORTANT_VIEWS} -->
+ <flag name="flagIncludeNotImportantViews" value="0x00000002" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
@@ -4317,6 +4337,7 @@
<li>"state_hovered"
<li>"state_drag_can_accept"
<li>"state_drag_hovered"
+ <li>"state_accessibility_focused"
</ul> -->
<declare-styleable name="DrawableStates">
<!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
@@ -4377,6 +4398,9 @@
indicating that a drag operation (for which the Drawable's view is a valid recipient)
is currently positioned over the Drawable. -->
<attr name="state_drag_hovered" format="boolean" />
+ <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+ indicating that a View has accessibility focus. -->
+ <attr name="state_accessibility_focused" format="boolean" />
</declare-styleable>
<declare-styleable name="ViewDrawableStates">
<attr name="state_pressed" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d7def44..4a5e442 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -226,6 +226,7 @@
<java-symbol type="attr" name="windowFixedWidthMinor" />
<java-symbol type="attr" name="windowFixedHeightMajor" />
<java-symbol type="attr" name="windowFixedHeightMinor" />
+ <java-symbol type="attr" name="accessibilityFocusedDrawable"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
@@ -787,11 +788,17 @@
<java-symbol type="string" name="sipAddressTypeHome" />
<java-symbol type="string" name="sipAddressTypeOther" />
<java-symbol type="string" name="sipAddressTypeWork" />
- <java-symbol type="string" name="sms_control_default_app_name" />
<java-symbol type="string" name="sms_control_message" />
- <java-symbol type="string" name="sms_control_no" />
<java-symbol type="string" name="sms_control_title" />
+ <java-symbol type="string" name="sms_control_no" />
<java-symbol type="string" name="sms_control_yes" />
+ <java-symbol type="string" name="sms_premium_short_code_confirm_message" />
+ <java-symbol type="string" name="sms_premium_short_code_confirm_title" />
+ <java-symbol type="string" name="sms_short_code_confirm_allow" />
+ <java-symbol type="string" name="sms_short_code_confirm_deny" />
+ <java-symbol type="string" name="sms_short_code_confirm_message" />
+ <java-symbol type="string" name="sms_short_code_confirm_report" />
+ <java-symbol type="string" name="sms_short_code_confirm_title" />
<java-symbol type="string" name="submit" />
<java-symbol type="string" name="sync_binding_label" />
<java-symbol type="string" name="sync_do_nothing" />
@@ -1095,7 +1102,9 @@
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="time_zones_by_country" />
+ <java-symbol type="xml" name="sms_short_codes" />
+ <java-symbol type="raw" name="accessibility_gestures" />
<java-symbol type="raw" name="incognito_mode_start_page" />
<java-symbol type="raw" name="loaderror" />
<java-symbol type="raw" name="nodomain" />
@@ -3576,7 +3585,11 @@
<public type="attr" name="layout_marginEnd"/>
<public type="attr" name="kcm"/>
+
<public type="attr" name="parentActivityName" />
<public type="attr" name="supportsSentenceSpellCheck" />
+
+ <public type="attr" name="importantForAccessibility"/>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 968b51c..e00986c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2789,16 +2789,30 @@
<string name="select_character">Insert character</string>
<!-- SMS per-application rate control Dialog --> <skip />
- <!-- See SMS_DIALOG. This is shown if the current application's name cannot be figuerd out. -->
- <string name="sms_control_default_app_name">Unknown app</string>
<!-- SMS_DIALOG: An SMS dialog is shown if an application tries to send too many SMSes. This is the title of that dialog. -->
<string name="sms_control_title">Sending SMS messages</string>
- <!-- See SMS_DIALOG. This is the message shown in that dialog. -->
- <string name="sms_control_message">A large number of SMS messages are being sent. Touch OK to continue, or Cancel to stop sending.</string>
- <!-- See SMS_DIALOG. This is a button choice to allow sending the SMSes. -->
- <string name="sms_control_yes">OK</string>
- <!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes.. -->
- <string name="sms_control_no">Cancel</string>
+ <!-- See SMS_DIALOG. This is the message shown in that dialog. [CHAR LIMIT=NONE] -->
+ <string name="sms_control_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> is sending a large number of SMS messages. Do you want to allow this app to continue sending messages?</string>
+ <!-- See SMS_DIALOG. This is a button choice to allow sending the SMSes. [CHAR LIMIT=30] -->
+ <string name="sms_control_yes">Allow</string>
+ <!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes. [CHAR LIMIT=30] -->
+ <string name="sms_control_no">Deny</string>
+
+ <!-- SMS short code verification dialog. --> <skip />
+ <!-- The dialog title for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_title">Send SMS to short code?</string>
+ <!-- The dialog title for the SMS premium short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_premium_short_code_confirm_title">Send premium SMS?</string>
+ <!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="sms_short_code_confirm_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="dest_address">%2$s</xliff:g></b>, which appears to be an SMS short code.<p>Sending text messages to some short codes may cause your mobile account to be billed for premium services.<p>Do you want to allow this app to send the message?</string>
+ <!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="sms_premium_short_code_confirm_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="dest_address">%2$s</xliff:g></b>, which is a premium SMS short code.<p><b>Sending a message to this destination will cause your mobile account to be billed for premium services.</b><p>Do you want to allow this app to send the message?</string>
+ <!-- Text of the approval button for the SMS short code confirmation dialog. [CHAR LIMIT=50] -->
+ <string name="sms_short_code_confirm_allow">Send message</string>
+ <!-- Text of the cancel button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_deny">Don\'t send</string>
+ <!-- Text of the button for the SMS short code confirmation dialog to report a malicious app. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_report">Report malicious app</string>
<!-- SIM swap and device reboot Dialog --> <skip />
<!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. -->
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 7e06e24..71738ad 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -366,6 +366,9 @@
<!-- Pointer style -->
<item name="pointerStyle">@android:style/Pointer</item>
+
+ <!-- Accessibility focused drawable. -->
+ <item name="accessibilityFocusedDrawable">@android:drawable/view_accessibility_focused</item>
</style>
<!-- Variant of {@link #Theme} with no title bar -->
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
new file mode 100644
index 0000000..8b395af
--- /dev/null
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Regex patterns for SMS short codes by country. -->
+<shortcodes>
+
+ <!-- The country attribute is the ISO country code of the user's account (from SIM card or NV).
+ The pattern attribute is a regex that matches all SMS short codes for the country.
+ The premium attribute is a regex that matches premium rate SMS short codes.
+ The free attribute matches short codes that we know will not cost the user, such as
+ emergency numbers. The standard attribute matches short codes that are billed at the
+ standard SMS rate. The user is warned when the destination phone number matches the
+ "pattern" or "premium" regexes, and does not match the "free" or "standard" regexes. -->
+
+ <!-- Harmonised European Short Codes are 6 digit numbers starting with 116 (free helplines).
+ Premium patterns include short codes from: http://aonebill.com/coverage&tariffs
+ and http://mobilcent.com/info-worldwide.asp and extracted from:
+ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
+
+ <!-- Albania: 5 digits, known short codes listed -->
+ <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
+
+ <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
+ <shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
+
+ <!-- Austria: 10 digits, premium prefix 09xx, plus EU -->
+ <shortcode country="at" pattern="11\\d{4}" premium="09.*" free="116\\d{3}" />
+
+ <!-- Australia: 6 or 8 digits starting with "19" -->
+ <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882" />
+
+ <!-- Azerbaijan: 4-5 digits, known premium codes listed -->
+ <shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+
+ <!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
+ <shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
+
+ <!-- Bulgaria: 4-5 digits, plus EU -->
+ <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}" />
+
+ <!-- Belarus: 4 digits -->
+ <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
+
+ <!-- Canada: 5-6 digits -->
+ <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188" />
+
+ <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
+ <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111" />
+
+ <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
+ http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
+ <shortcode country="cn" premium="1066.*" free="1065.*" />
+
+ <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
+
+ <!-- Czech Republic: 7-8 digits, starting with 9, plus EU:
+ http://www.o2.cz/osobni/en/services-by-alphabet/91670-premium_sms.html -->
+ <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
+
+ <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}" />
+
+ <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
+ <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
+
+ <!-- Estonia: short codes 3-5 digits starting with 1, plus premium 7 digit numbers starting with 90, plus EU.
+ http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
+ <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}" />
+
+ <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
+ http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
+ <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}" />
+
+ <!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
+ <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
+
+ <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
+ http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements -->
+ <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}" />
+
+ <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
+ http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf -->
+ <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}" />
+
+ <!-- Georgia: 4 digits, known premium codes listed -->
+ <shortcode country="ge" pattern="\\d{4}" premium="801[234]|888[239]" />
+
+ <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
+ <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}" />
+
+ <!-- Hungary: 4 or 10 digits starting with 1 or 0, plus EU:
+ http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
+ <shortcode country="hu" pattern="[01](?:\\d{3}|\\d{9})" premium="0691227910|1784" free="116\\d{3}" />
+
+ <!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
+ http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
+ <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
+
+ <!-- Israel: 4 digits, known premium codes listed -->
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
+
+ <!-- Italy: 5 digits (premium=4xxxx), plus EU:
+ http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
+ <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}" />
+
+ <!-- Kyrgyzstan: 4 digits, known premium codes listed -->
+ <shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
+
+ <!-- Kazakhstan: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-kazakhstan/ -->
+ <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
+
+ <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
+ <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}" />
+
+ <!-- Luxembourg: 5 digits, 6xxxx, plus EU:
+ http://www.luxgsm.lu/assets/files/filepage/file_1253803400.pdf -->
+ <shortcode country="lu" premium="6\\d{4}" free="116\\d{3}" />
+
+ <!-- Latvia: 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="lv" pattern="\\d{4}" premium="18(?:19|63|7[1-4])" free="116\\d{3}" />
+
+ <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" />
+
+ <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
+ <shortcode country="my" pattern="\\d{5}" premium="32298|33776" />
+
+ <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}" />
+
+ <!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" />
+
+ <!-- New Zealand: 3-4 digits, known premium codes listed -->
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995" />
+
+ <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}" />
+
+ <!-- Portugal: 5 digits, plus EU:
+ http://clients.txtnation.com/entries/158326-portugal-premium-sms-short-code-regulations -->
+ <shortcode country="pt" premium="6[1289]\\d{3}" free="116\\d{3}" />
+
+ <!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
+ <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}" />
+
+ <!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
+ <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)" />
+
+ <!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
+ <shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
+
+ <!-- Singapore: 5 digits: http://clients.txtnation.com/entries/306442-singapore-premium-sms-short-code-requirements
+ Free government directory info at 74688: http://app.sgdi.gov.sg/sms_help.asp -->
+ <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688" />
+
+ <!-- Slovenia: 4 digits (premium=3xxx, 6xxx, 8xxx), plus EU: http://www.cmtelecom.com/premium-sms/slovenia -->
+ <shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
+
+ <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
+ <shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
+
+ <!-- Tajikistan: 4 digits, known premium codes listed -->
+ <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+
+ <!-- Ukraine: 4 digits, known premium codes listed -->
+ <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
+
+ <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm) -->
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" />
+
+</shortcodes>
diff --git a/core/tests/coretests/apks/install/Android.mk b/core/tests/coretests/apks/install/Android.mk
new file mode 100644
index 0000000..b38dc20
--- /dev/null
+++ b/core/tests/coretests/apks/install/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install/AndroidManifest.xml b/core/tests/coretests/apks/install/AndroidManifest.xml
new file mode 100644
index 0000000..60f1ba0
--- /dev/null
+++ b/core/tests/coretests/apks/install/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_loc">
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install/res/values/strings.xml b/core/tests/coretests/apks/install/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/core/tests/coretests/apks/install/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/core/tests/coretests/res/raw/install b/core/tests/coretests/res/raw/install
deleted file mode 100644
index 06981f4..0000000
--- a/core/tests/coretests/res/raw/install
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 9575ced..580b4da 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -170,11 +170,10 @@
return ipm;
}
- public boolean invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver) {
+ public void invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver,
+ boolean shouldSucceed) {
PackageInstallObserver observer = new PackageInstallObserver();
- final boolean received = false;
mContext.registerReceiver(receiver, receiver.filter);
- final boolean DEBUG = true;
try {
// Wait on observer
synchronized(observer) {
@@ -192,10 +191,24 @@
if(!observer.isDone()) {
fail("Timed out waiting for packageInstalled callback");
}
- if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- Log.i(TAG, "Failed to install with error code = " + observer.returnCode);
- return false;
+
+ if (shouldSucceed) {
+ if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ fail("Package installation should have succeeded, but got code "
+ + observer.returnCode);
+ }
+ } else {
+ if (observer.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ fail("Package installation should fail");
+ }
+
+ /*
+ * We'll never expect get a notification since we
+ * shouldn't succeed.
+ */
+ return;
}
+
// Verify we received the broadcast
waitTime = 0;
while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
@@ -209,7 +222,6 @@
if(!receiver.isDone()) {
fail("Timed out waiting for PACKAGE_ADDED notification");
}
- return receiver.received;
}
}
} finally {
@@ -588,7 +600,7 @@
}
} else {
InstallReceiver receiver = new InstallReceiver(pkg.packageName);
- assertTrue(invokeInstallPackage(packageURI, flags, receiver));
+ invokeInstallPackage(packageURI, flags, receiver, true);
// Verify installed information
assertInstall(pkg, flags, expInstallLocation);
}
@@ -705,13 +717,9 @@
receiver = new InstallReceiver(ip.pkg.packageName);
}
try {
- try {
- assertEquals(invokeInstallPackage(ip.packageURI, flags, receiver), replace);
- if (replace) {
- assertInstall(ip.pkg, flags, ip.pkg.installLocation);
- }
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
+ invokeInstallPackage(ip.packageURI, flags, receiver, replace);
+ if (replace) {
+ assertInstall(ip.pkg, flags, ip.pkg.installLocation);
}
} finally {
cleanUpInstall(ip);
@@ -1244,7 +1252,7 @@
GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
- assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags, receiver), true);
+ invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
} catch (Exception e) {
failStr("Failed with exception : " + e);
@@ -1271,7 +1279,7 @@
GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
- assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags, receiver), true);
+ invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
} catch (Exception e) {
failStr("Failed with exception : " + e);
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
index 3dba4e5..1968a32 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
@@ -19,7 +19,7 @@
import android.widget.focus.ListOfButtons;
import com.android.frameworks.coretests.R;
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.widget.ListAdapter;
import android.widget.Button;
@@ -31,7 +31,7 @@
* Tests that focus works as expected when navigating into and out of
* a {@link ListView} that has buttons in it.
*/
-public class ListOfButtonsTest extends ActivityInstrumentationTestCase<ListOfButtons> {
+public class ListOfButtonsTest extends ActivityInstrumentationTestCase2<ListOfButtons> {
private ListAdapter mListAdapter;
private Button mButtonAtTop;
@@ -39,7 +39,7 @@
private ListView mListView;
public ListOfButtonsTest() {
- super("com.android.frameworks.coretests", ListOfButtons.class);
+ super(ListOfButtons.class);
}
@Override
@@ -47,6 +47,7 @@
super.setUp();
ListOfButtons a = getActivity();
+ getInstrumentation().waitForIdleSync();
mListAdapter = a.getListAdapter();
mButtonAtTop = (Button) a.findViewById(R.id.button);
mListView = a.getListView();
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index 2e18a10..e403205 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -20,3 +20,5 @@
$(call inherit-product, frameworks/base/data/sounds/AudioPackage4.mk)
$(call inherit-product, frameworks/base/data/sounds/AudioPackage5.mk)
$(call inherit-product, frameworks/base/data/sounds/AudioPackage6.mk)
+$(call inherit-product, frameworks/base/data/sounds/AudioPackage7.mk)
+$(call inherit-product, frameworks/base/data/sounds/AudioPackage7alt.mk)
diff --git a/docs/html/images/training/backward-compatible-ui-classes-eclair.png b/docs/html/images/training/backward-compatible-ui-classes-eclair.png
new file mode 100644
index 0000000..febba5b
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes-eclair.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png
new file mode 100644
index 0000000..ba14252
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-classes.png b/docs/html/images/training/backward-compatible-ui-classes.png
new file mode 100644
index 0000000..c5a3cd8
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-classes.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-gb.png b/docs/html/images/training/backward-compatible-ui-gb.png
new file mode 100644
index 0000000..621ee63
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-gb.png
Binary files differ
diff --git a/docs/html/images/training/backward-compatible-ui-ics.png b/docs/html/images/training/backward-compatible-ui-ics.png
new file mode 100644
index 0000000..6460554
--- /dev/null
+++ b/docs/html/images/training/backward-compatible-ui-ics.png
Binary files differ
diff --git a/docs/html/images/training/implementing-navigation-up.png b/docs/html/images/training/implementing-navigation-up.png
new file mode 100644
index 0000000..18f3779
--- /dev/null
+++ b/docs/html/images/training/implementing-navigation-up.png
Binary files differ
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 5297c23..686bde3 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -227,7 +227,31 @@
</li>
</ul>
</li>
-
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/backward-compatible-ui/index.html">
+ <span class="en">Creating Backward-Compatible UIs<span class="new"> new!</span></span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/abstracting.html">
+ <span class="en">Abstracting the New APIs</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/new-implementation.html">
+ <span class="en">Proxying to the New APIs</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/older-implementation.html">
+ <span class="en">Creating an Implementation with Older APIs</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/backward-compatible-ui/using-component.html">
+ <span class="en">Using the Version-Aware Component</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/enterprise/index.html">
<span class="en">Developing for Enterprise</span>
@@ -278,7 +302,32 @@
</a>
</li>
</ul>
- </li>
+ </li>
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/implementing-navigation/index.html">
+ <span class="en">Implementing Effective Navigation<span class="new"> new!</span></span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/implementing-navigation/lateral.html">
+ <span class="en">Implementing Lateral Navigation</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/implementing-navigation/ancestral.html">
+ <span class="en">Implementing Ancestral Navigation</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/implementing-navigation/temporal.html">
+ <span class="en">Implementing Temporal Navigation</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/implementing-navigation/descendant.html">
+ <span class="en">Implementing Descendant Navigation</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/tv/index.html">
<span class="en">Designing for TV<span class="new"> new!</span></span>
diff --git a/docs/html/shareables/training/EffectiveNavigation.zip b/docs/html/shareables/training/EffectiveNavigation.zip
new file mode 100644
index 0000000..f21af45
--- /dev/null
+++ b/docs/html/shareables/training/EffectiveNavigation.zip
Binary files differ
diff --git a/docs/html/shareables/training/TabCompat.zip b/docs/html/shareables/training/TabCompat.zip
new file mode 100644
index 0000000..b70b442
--- /dev/null
+++ b/docs/html/shareables/training/TabCompat.zip
Binary files differ
diff --git a/docs/html/training/backward-compatible-ui/abstracting.jd b/docs/html/training/backward-compatible-ui/abstracting.jd
new file mode 100644
index 0000000..1141b54
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/abstracting.jd
@@ -0,0 +1,111 @@
+page.title=Abstracting the New APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Proxying to the New APIs
+next.link=new-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ul>
+ <li><a href="#prepare-abstraction">Prepare for Abstraction</a></li>
+ <li><a href="#create-abstract-tab">Create an Abstract Tab Interface</a></li>
+ <li><a href="#abstract-actionbar-tab">Abstract ActionBar.Tab</a></li>
+ <li><a href="#abstract-actionbar-methods">Abstract ActionBar Tab Methods</a></li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+ class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Suppose you want to use <a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">action bar tabs</a> as the primary form of top-level navigation in your application. Unfortunately, the {@link android.app.ActionBar} APIs are only available in Android 3.0 or later (API level 11+). Thus, if you want to distribute your application to devices running earlier versions of the platform, you need to provide an implementation that supports the newer API while providing a fallback mechanism that uses older APIs.</p>
+
+<p>In this class, you build a tabbed user interface (UI) component that uses abstract classes with version-specific implementations to provide backward-compatibility. This lesson describes how to create an abstraction layer for the new tab APIs as the first step toward building the tab component.</p>
+
+<h2 id="prepare-abstraction">Prepare for Abstraction</h2>
+
+<p><a href="http://en.wikipedia.org/wiki/Abstraction_(computer_science)">Abstraction</a> in the Java programming language involves the creation of one or more interfaces or abstract classes to hide implementation details. In the case of newer Android APIs, you can use abstraction to build version-aware components that use the current APIs on newer devices, and fallback to older, more compatible APIs on older devices.</p>
+
+<p>When using this approach, you first determine what newer classes you want to be able to use in a backward compatible way, then create abstract classes, based on the public interfaces of the newer classes. In defining the abstraction interfaces, you should mirror the newer API as much as possible. This maximizes forward-compatibility and makes it easier to drop the abstraction layer in the future when it is no longer necessary.</p>
+
+<p>After creating abstract classes for these new APIs, any number of implementations can be created and chosen at runtime. For the purposes of backward-compatibility, these implementations can vary by required API level. Thus, one implementation may use recently released APIs, while others can use older APIs.</p>
+
+<h2 id="create-abstract-tab">Create an Abstract Tab Interface</h2>
+
+<p>In order to create a backward-compatible version of tabs, you should first determine which features and specific APIs your application requires. In the case of top-level section tabs, suppose you have the following functional requirements:</p>
+
+<ol>
+<li>Tab indicators should show text and an icon.</li>
+<li>Tabs can be associated with a fragment instance.</li>
+<li>The activity should be able to listen for tab changes.</li>
+</ol>
+
+<p>Preparing these requirements in advance allows you to control the scope of your abstraction layer. This means that you can spend less time creating multiple implementations of your abstraction layer and begin using your new backward-compatible implementation sooner.</p>
+
+<p>The key APIs for tabs are in {@link android.app.ActionBar} and {@link android.app.ActionBar.Tab ActionBar.Tab}. These are the APIs to abstract in order to make your tabs version-aware. The requirements for this example project call for compatibility back to Eclair (API level 5) while taking advantage of the new tab features in Honeycomb (API Level 11). A diagram of the class structure to support these two implementations and their abstract base classes (or interfaces) is shown below.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes.png"
+ alt="Class diagram of abstract base classes and version-specific implementations." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram of abstract base classes and version-specific implementations.</p>
+
+<h2 id="abstract-actionbar-tab">Abstract ActionBar.Tab</h2>
+
+<p>Get started on building your tab abstraction layer by creating an abstract class representing a tab, that mirrors the {@link android.app.ActionBar.Tab ActionBar.Tab} interface:</p>
+
+<pre>
+public abstract class CompatTab {
+ ...
+ public abstract CompatTab setText(int resId);
+ public abstract CompatTab setIcon(int resId);
+ public abstract CompatTab setTabListener(
+ CompatTabListener callback);
+ public abstract CompatTab setFragment(Fragment fragment);
+
+ public abstract CharSequence getText();
+ public abstract Drawable getIcon();
+ public abstract CompatTabListener getCallback();
+ public abstract Fragment getFragment();
+ ...
+}
+</pre>
+
+<p>You can use an abstract class instead of an interface here to simplify the implementation of common features such as association of tab objects with activities (not shown in the code snippet).</p>
+
+<h2 id="abstract-actionbar-methods">Abstract ActionBar Tab Methods</h2>
+
+<p>Next, define an abstract class that allows you to create and add tabs to an activity, like {@link android.app.ActionBar#newTab ActionBar.newTab()} and {@link android.app.ActionBar#addTab ActionBar.addTab()}:</p>
+
+<pre>
+public abstract class TabHelper {
+ ...
+
+ public CompatTab newTab(String tag) {
+ // This method is implemented in a later lesson.
+ }
+
+ public abstract void addTab(CompatTab tab);
+
+ ...
+}
+</pre>
+
+<p>In the next lessons, you create implementations for <code>TabHelper</code> and <code>CompatTab</code> that work across both older and newer platform versions.</p>
diff --git a/docs/html/training/backward-compatible-ui/index.jd b/docs/html/training/backward-compatible-ui/index.jd
new file mode 100644
index 0000000..7e27e68
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/index.jd
@@ -0,0 +1,57 @@
+page.title=Creating Backward-Compatible UIs
+
+trainingnavtop=true
+startpage=true
+next.title=Abstracting the New Implementation
+next.link=abstracting.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+ <li>API level 5</li>
+ <li><a href="{@docRoot}sdk/compatibility-library.html">The Android Support Package</a></li>
+</ul>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}resources/samples/ActionBarCompat/index.html">ActionBarCompat</a></li>
+ <li><a href="http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html">How to have your (Cup)cake and eat it too</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+ class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions of the platform.</p>
+
+<p>Throughout this class, the new <a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a> feature introduced in Android 3.0 (API level 11) serves as the guiding example, but you can apply these techniques to other UI components and API features.</p>
+
+<h2 id="lessons">Lessons</h2>
+
+
+<dl>
+ <dt><strong><a href="abstracting.html">Abstracting the New APIs</a></strong></dt>
+ <dd>Determine which features and APIs your application needs. Learn how to define application-specific, intermediary Java interfaces that abstract the implementation of the UI component to your application.</dd>
+
+ <dt><strong><a href="new-implementation.html">Proxying to the New APIs</a></strong></dt>
+ <dd>Learn how to create an implementation of your interface that uses newer APIs.</dd>
+
+ <dt><strong><a href="older-implementation.html">Creating an Implementation with Older APIs</a></strong></dt>
+ <dd>Learn how to create a custom implementation of your interface that uses older APIs.</dd>
+
+ <dt><strong><a href="using-component.html">Using the Version-Aware Component</a></strong></dt>
+ <dd>Learn how to choose an implementation to use at runtime, and begin using the interface in your application.</dd>
+</dl>
diff --git a/docs/html/training/backward-compatible-ui/new-implementation.jd b/docs/html/training/backward-compatible-ui/new-implementation.jd
new file mode 100644
index 0000000..5b8b51c
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/new-implementation.jd
@@ -0,0 +1,113 @@
+page.title=Proxying to the New APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Abstracting the New APIs
+previous.link=abstracting.html
+next.title=Creating an Implementation with Older APIs
+next.link=older-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#new-tabs">Implement Tabs Using New APIs</a></li>
+ <li><a href="#compattabhoneycomb">Implement CompatTabHoneycomb</a></li>
+ <li><a href="#tabhelperhoneycomb">Implement TabHelperHoneycomb</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html#Tabs">Action Bar Tabs</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+ class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This lesson shows you how to subclass the <code>CompatTab</code> and <code>TabHelper</code> abstract classes and use new APIs. Your application can use this implementation on devices running a platform version that supports them.</p>
+
+<h2 id="new-tabs">Implement Tabs Using New APIs</h2>
+
+<p>The concrete classes for <code>CompatTab</code> and <code>TabHelper</code> that use newer APIs are a <em>proxy</em> implementation. Since the abstract classes defined in the previous lesson mirror the new APIs (class structure, method signatures, etc.), the concrete classes that use these newer APIs simply proxy method calls and their results.</p>
+
+<p>You can directly use newer APIs in these concrete classes—and not crash on earlier devices—because of lazy class loading. Classes are loaded and initialized on first access—instantiating the class or accessing one of its static fields or methods for the first time. Thus, as long as you don't instantiate the Honeycomb-specific implementations on pre-Honeycomb devices, the Dalvik VM won't throw any {@link java.lang.VerifyError} exceptions.</p>
+
+<p>A good naming convention for this implementation is to append the API level or platform version code name corresponding to the APIs required by the concrete classes. For example, the native tab implementation can be provided by <code>CompatTabHoneycomb</code> and <code>TabHelperHoneycomb</code> classes, since they rely on APIs available in Android 3.0 (API level 11) or later.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes-honeycomb.png"
+ alt="Class diagram for the Honeycomb implementation of tabs." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram for the Honeycomb implementation of tabs.</p>
+
+<h2 id="compattabhoneycomb">Implement CompatTabHoneycomb</h2>
+
+<p><code>CompatTabHoneycomb</code> is the implementation of the <code>CompatTab</code> abstract class that <code>TabHelperHoneycomb</code> uses to reference individual tabs. <code>CompatTabHoneycomb</code> simply proxies all method calls to its contained {@link android.app.ActionBar.Tab} object.</p>
+
+<p>Begin implementing <code>CompatTabHoneycomb</code> using the new {@link android.app.ActionBar.Tab ActionBar.Tab} APIs:</p>
+
+<pre>
+public class CompatTabHoneycomb extends CompatTab {
+ // The native tab object that this CompatTab acts as a proxy for.
+ ActionBar.Tab mTab;
+ ...
+
+ protected CompatTabHoneycomb(FragmentActivity activity, String tag) {
+ ...
+ // Proxy to new ActionBar.newTab API
+ mTab = activity.getActionBar().newTab();
+ }
+
+ public CompatTab setText(int resId) {
+ // Proxy to new ActionBar.Tab.setText API
+ mTab.setText(resId);
+ return this;
+ }
+
+ ...
+ // Do the same for other properties (icon, callback, etc.)
+}
+</pre>
+
+<h2 id="tabhelperhoneycomb">Implement TabHelperHoneycomb</h2>
+
+<p><code>TabHelperHoneycomb</code> is the implementation of the <code>TabHelper</code> abstract class that proxies method calls to an actual {@link android.app.ActionBar}, obtained from its contained {@link android.app.Activity}.</p>
+
+<p>Implement <code>TabHelperHoneycomb</code>, proxying method calls to the {@link android.app.ActionBar} API:</p>
+
+<pre>
+public class TabHelperHoneycomb extends TabHelper {
+ ActionBar mActionBar;
+ ...
+
+ protected void setUp() {
+ if (mActionBar == null) {
+ mActionBar = mActivity.getActionBar();
+ mActionBar.setNavigationMode(
+ ActionBar.NAVIGATION_MODE_TABS);
+ }
+ }
+
+ public void addTab(CompatTab tab) {
+ ...
+ // Tab is a CompatTabHoneycomb instance, so its
+ // native tab object is an ActionBar.Tab.
+ mActionBar.addTab((ActionBar.Tab) tab.getTab());
+ }
+
+ // The other important method, newTab() is part of
+ // the base implementation.
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/backward-compatible-ui/older-implementation.jd b/docs/html/training/backward-compatible-ui/older-implementation.jd
new file mode 100644
index 0000000..5006123
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/older-implementation.jd
@@ -0,0 +1,126 @@
+page.title=Creating an Implementation with Older APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Proxying to the New APIs
+previous.link=new-implementation.html
+next.title=Using the Version-Aware Component
+next.link=using-component.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#decide-substitute">Decide on a Substitute Solution</a></li>
+ <li><a href="#older-tabs">Implement Tabs Using Older APIs</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+ class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p>This lesson discusses how to create an implementation that mirrors newer APIs yet supports older devices.</p>
+
+<h2 id="decide-substitute">Decide on a Substitute Solution</h2>
+
+<p>The most challenging task in using newer UI features in a backward-compatible way is deciding on and implementing an older (fallback) solution for older platform versions. In many cases, it's possible to fulfill the purpose of these newer UI components using older UI framework features. For example:</p>
+
+<ul>
+
+<li>
+<p>Action bars can be implemented using a horizontal {@link android.widget.LinearLayout} containing image buttons, either as custom title bars or as views in your activity layout. Overflow actions can be presented under the device <em>Menu</em> button.</p>
+</li>
+
+<li>
+<p>Action bar tabs can be implemented using a horizontal {@link android.widget.LinearLayout} containing buttons, or using the {@link android.widget.TabWidget} UI element.</p>
+</li>
+
+<li>
+<p>{@link android.widget.NumberPicker} and {@link android.widget.Switch} widgets can be implemented using {@link android.widget.Spinner} and {@link android.widget.ToggleButton} widgets, respectively.</p>
+</li>
+
+<li>
+<p>{@link android.widget.ListPopupWindow} and {@link android.widget.PopupMenu} widgets can be implemented using {@link android.widget.PopupWindow} widgets.</p>
+</li>
+
+</ul>
+
+<p>There generally isn't a one-size-fits-all solution for backporting newer UI components to older devices. Be mindful of the user experience: on older devices, users may not be familiar with newer design patterns and UI components. Give some thought as to how the same functionality can be delivered using familiar elements. In many cases this is less of a concern—if newer UI components are prominent in the application ecosystem (such as the action bar), or where the interaction model is extremely simple and intuitive (such as swipe views using a {@link android.support.v4.view.ViewPager}).</p>
+
+<h2 id="older-tabs">Implement Tabs Using Older APIs</h2>
+
+<p>To create an older implementation of action bar tabs, you can use a {@link android.widget.TabWidget} and {@link android.widget.TabHost} (although one can alternatively use horizontally laid-out {@link android.widget.Button} widgets). Implement this in classes called <code>TabHelperEclair</code> and <code>CompatTabEclair</code>, since this implementation uses APIs introduced no later than Android 2.0 (Eclair).</p>
+
+
+<img src="{@docRoot}images/training/backward-compatible-ui-classes-eclair.png"
+ alt="Class diagram for the Eclair implementation of tabs." id="figure-classes">
+
+<p class="img-caption"><strong>Figure 1.</strong> Class diagram for the Eclair implementation of tabs.</p>
+
+<p>The <code>CompatTabEclair</code> implementation stores tab properties such as the tab text and icon in instance variables, since there isn't an {@link android.app.ActionBar.Tab ActionBar.Tab} object available to handle this storage:</p>
+
+<pre>
+public class CompatTabEclair extends CompatTab {
+ // Store these properties in the instance,
+ // as there is no ActionBar.Tab object.
+ private CharSequence mText;
+ ...
+
+ public CompatTab setText(int resId) {
+ // Our older implementation simply stores this
+ // information in the object instance.
+ mText = mActivity.getResources().getText(resId);
+ return this;
+ }
+
+ ...
+ // Do the same for other properties (icon, callback, etc.)
+}
+</pre>
+
+<p>The <code>TabHelperEclair</code> implementation makes use of methods on the
+{@link android.widget.TabHost} widget for creating {@link android.widget.TabHost.TabSpec}
+objects and tab indicators:</p>
+
+<pre>
+public class TabHelperEclair extends TabHelper {
+ private TabHost mTabHost;
+ ...
+
+ protected void setUp() {
+ if (mTabHost == null) {
+ // Our activity layout for pre-Honeycomb devices
+ // must contain a TabHost.
+ mTabHost = (TabHost) mActivity.findViewById(
+ android.R.id.tabhost);
+ mTabHost.setup();
+ }
+ }
+
+ public void addTab(CompatTab tab) {
+ ...
+ TabSpec spec = mTabHost
+ .newTabSpec(tag)
+ .setIndicator(tab.getText()); // And optional icon
+ ...
+ mTabHost.addTab(spec);
+ }
+
+ // The other important method, newTab() is part of
+ // the base implementation.
+}
+</pre>
+
+<p>You now have two implementations of <code>CompatTab</code> and <code>TabHelper</code>: one that works on devices running Android 3.0 or later and uses new APIs, and another that works on devices running Android 2.0 or later and uses older APIs. The next lesson discusses using these implementations in your application.</p>
diff --git a/docs/html/training/backward-compatible-ui/using-component.jd b/docs/html/training/backward-compatible-ui/using-component.jd
new file mode 100644
index 0000000..4bf7fa0
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/using-component.jd
@@ -0,0 +1,143 @@
+page.title=Using the Version-Aware Component
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Creating an Implementation with Older APIs
+previous.link=older-implementation.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#switching-logic">Add the Switching Logic</a></li>
+ <li><a href="#layout">Create a Version-Aware Activity Layout</a></li>
+ <li><a href="#use-tabhelper">Use TabHelper in Your Activity</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/TabCompat.zip"
+ class="button">Download the sample app</a>
+<p class="filename">TabCompat.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Now that you have two implementations of <code>TabHelper</code> and <code>CompatTab</code>—one for Android 3.0 and later and one for earlier versions of the platform—it's time to do something with these implementations. This lesson discusses creating the logic for switching between these implementations, creating version-aware layouts, and finally using the backward-compatible UI component.</p>
+
+<h2 id="switching-logic">Add the Switching Logic</h2>
+
+<p>The <code>TabHelper</code> abstract class acts as a <a href="http://en.wikipedia.org/wiki/Factory_(software_concept)">factory</a> for creating version-appropriate <code>TabHelper</code> and <code>CompatTab</code> instances, based on the current device's platform version:</p>
+
+<pre>
+public abstract class TabHelper {
+ ...
+ // Usage is TabHelper.createInstance(activity)
+ public static TabHelper createInstance(FragmentActivity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return new TabHelperHoneycomb(activity);
+ } else {
+ return new TabHelperEclair(activity);
+ }
+ }
+
+ // Usage is mTabHelper.newTab("tag")
+ public CompatTab newTab(String tag) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return new CompatTabHoneycomb(mActivity, tag);
+ } else {
+ return new CompatTabEclair(mActivity, tag);
+ }
+ }
+ ...
+}
+</pre>
+
+<h2 id="layout">Create a Version-Aware Activity Layout</h2>
+
+<p>The next step is to provide layouts for your activity that can support the two tab implementations. For the older implementation (<code>TabHelperEclair</code>), you need to ensure that your activity layout contains a {@link android.widget.TabWidget} and {@link android.widget.TabHost}, along with a container for tab contents:</p>
+
+<p><strong>res/layout/main.xml:</strong></p>
+
+<pre>
+<!-- This layout is for API level 5-10 only. -->
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="5dp">
+
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+</TabHost>
+</pre>
+
+<p>For the <code>TabHelperHoneycomb</code> implementation, all you need is a {@link android.widget.FrameLayout} to contain the tab contents, since the tab indicators are provided by the {@link android.app.ActionBar}:</p>
+
+<p><strong>res/layout-v11/main.xml:</strong></p>
+
+<pre>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</pre>
+
+<p>At runtime, Android will decide which version of the <code>main.xml</code> layout to inflate depending on the platform version. This is the same logic shown in the previous section to determine which <code>TabHelper</code> implementation to use.</p>
+
+<h2 id="use-tabhelper">Use TabHelper in Your Activity</h2>
+
+<p>In your activity's {@link android.app.Activity#onCreate onCreate()} method, you can obtain a <code>TabHelper</code> object and add tabs with the following code:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ setContentView(R.layout.main);
+
+ TabHelper tabHelper = TabHelper.createInstance(this);
+ tabHelper.setUp();
+
+ CompatTab photosTab = tabHelper
+ .newTab("photos")
+ .setText(R.string.tab_photos);
+ tabHelper.addTab(photosTab);
+
+ CompatTab videosTab = tabHelper
+ .newTab("videos")
+ .setText(R.string.tab_videos);
+ tabHelper.addTab(videosTab);
+}
+</pre>
+
+<p>When running the application, this code inflates the correct activity layout and instantiates either a <code>TabHelperHoneycomb</code> or <code>TabHelperEclair</code> object. The concrete class that's actually used is opaque to the activity, since they share the common <code>TabHelper</code> interface.</p>
+
+<p>Below are two screenshots of this implementation running on an Android 2.3 and Android 4.0 device.</p>
+
+<img src="{@docRoot}images/training/backward-compatible-ui-gb.png"
+ alt="Example screenshot of tabs running on an Android 2.3 device (using TabHelperEclair)." width="200">
+
+<img src="{@docRoot}images/training/backward-compatible-ui-ics.png"
+ alt="Example screenshots of tabs running on an Android 4.0 device (using TabHelperHoneycomb)." width="200">
+
+<p class="img-caption"><strong>Figure 1.</strong> Example screenshots of backward-compatible tabs running on an Android 2.3 device (using <code>TabHelperEclair</code>) and an Android 4.0 device (using <code>TabHelperHoneycomb</code>).</p>
diff --git a/docs/html/training/implementing-navigation/ancestral.jd b/docs/html/training/implementing-navigation/ancestral.jd
new file mode 100644
index 0000000..495b45d
--- /dev/null
+++ b/docs/html/training/implementing-navigation/ancestral.jd
@@ -0,0 +1,124 @@
+page.title=Implementing Ancestral Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Lateral Navigation
+previous.link=lateral.html
+next.title=Implementing Temporal Navigation
+next.link=temporal.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#up">Implement <em>Up</em> Navigation</a></li>
+ <li><a href="#app-home">Properly Handle the Application Home Screen</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li>
+ <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a></li>
+ <li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+ class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Ancestral navigation</em> is up the application's information hierarchy, where the top of the hierarchy (or root) is the application's home screen. This navigation concept is described in <a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Designing Effective Navigation</a>. This lesson discusses how to provide ancestral navigation using the <em>Up</em> button in the action bar.</p>
+
+
+<h2 id="up">Implement <em>Up</em> Navigation</h2>
+
+<p>When implementing ancestral navigation, all screens in your application that aren't the home screen should offer a means of navigating to the immediate parent screen in the hierarchy via the <em>Up</em> button in the action bar.</p>
+
+
+<img src="{@docRoot}images/training/implementing-navigation-up.png"
+ alt="The Up button in the action bar." id="figure-up">
+
+<p class="img-caption"><strong>Figure 1.</strong> The <em>Up</em> button in the action bar.</p>
+
+<p>Regardless of how the current screen was reached, pressing this button should always take the user to the same screen in the hierarchy.</p>
+
+<p>To implement <em>Up</em>, enable it in the action bar in your activity's {@link android.app.Activity#onCreate onCreate()} method:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ ...
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ ...
+}
+</pre>
+
+<p>You should also handle <code>android.R.id.home</code> in {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()}. This resource is the menu item ID for the <em>Home</em> (or <em>Up</em>) button. To ensure that a specific parent activity is shown, <em>DO NOT</em> simply call {@link android.app.Activity#finish finish()}. Instead, use an intent such as the one described below.</p>
+
+<pre>
+{@literal @}Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // This is called when the Home (Up) button is pressed
+ // in the Action Bar.
+ Intent parentActivityIntent = new Intent(this, MyParentActivity.class);
+ parentActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(parentActivityIntent);
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<p>When the current activity belongs to a task from a different application—for example if it was reached via an intent from another application—pressing <em>Up</em> should create a new task for the application with a synthesized back stack. This approach is described in <a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a> and the {@link android.support.v4.app.TaskStackBuilder} class reference.</p>
+
+<p>The {@link android.support.v4.app.NavUtils} and {@link android.support.v4.app.TaskStackBuilder} classes in the <a href="{@docRoot}sdk/compatibility-library.html">Android Support Package</a> provide helpers for implementing this behavior correctly. An example usage of these two helper classes is below:</p>
+
+<pre>
+{@literal @}Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ Intent upIntent = new Intent(this, MyParentActivity.class);
+ if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
+ // This activity is not part of the application's task, so create a new task
+ // with a synthesized back stack.
+ TaskStackBuilder.from(this)
+ .addNextIntent(new Intent(this, MyGreatGrandParentActivity.class))
+ .addNextIntent(new Intent(this, MyGrandParentActivity.class))
+ .addNextIntent(upIntent)
+ .startActivities();
+ finish();
+ } else {
+ // This activity is part of the application's task, so simply
+ // navigate up to the hierarchical parent activity.
+ NavUtils.navigateUpTo(this, upIntent);
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<h2 id="app-home">Properly Handle the Application Home Screen</h2>
+
+<p>By default, the <em>Home</em> button in the action bar is interactive. Since it does not make much sense to navigate home—or up one level—while on the home screen, you should disable the button like so:</p>
+
+<pre>
+getActionBar().setHomeButtonEnabled(false);
+</pre>
diff --git a/docs/html/training/implementing-navigation/descendant.jd b/docs/html/training/implementing-navigation/descendant.jd
new file mode 100644
index 0000000..7d0063c
--- /dev/null
+++ b/docs/html/training/implementing-navigation/descendant.jd
@@ -0,0 +1,65 @@
+page.title=Implementing Descendant Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Temporal Navigation
+previous.link=temporal.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#master-detail">Implement Master/Detail Flows Across Handsets and Tablets</a></li>
+ <li><a href="#external-activities">Navigate into External Activities</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/design-navigation/descendant-lateral.html">Providing Descendant and Lateral Navigation</a></li>
+ <li><a href="{@docRoot}design/patterns/app-structure.html">Android Design: App Structure</a></li>
+ <li><a href="{@docRoot}design/patterns/multi-pane-layouts.html">Android Design: Multi-pane Layouts</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+ class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Descendant navigation</em> is navigation down the application's information hierarchy. This is described in <a href="{@docRoot}training/design-navigation/descendant-lateral.html">Designing Effective Navigation</a> and also covered in <a href="{@docRoot}design/patterns/app-structure.html">Android Design: Application Structure</a>.</p>
+
+<p>Descendant navigation is usually implemented using {@link android.content.Intent} objects and {@link android.content.Context#startActivity startActivity()}, or by adding fragments to an activity using {@link android.app.FragmentTransaction} objects. This lesson covers other interesting cases that arise when implementing descendant navigation.</p>
+
+<h2 id="master-detail">Implement Master/Detail Flows Across Handsets and Tablets</h2>
+
+<p>In a <em>master/detail</em> navigation flow, a <em>master</em> screen contains a list of items in a collection, and a <em>detail</em> screen shows detailed information about a specific item within that collection. Implementing navigation from the master screen to the detail screen is one form of descendant navigation.</p>
+
+<p>Handset touchscreens are most suitable for displaying one screen at a time (either the master or the detail screen); this concern is further discussed in <a href="{@docRoot}training/design-navigation/multiple-sizes.html">Planning for Multiple Touchscreen Sizes</a>. Descendant navigation in this case is often implemented using an {@link android.content.Intent} that starts an activity representing the detail screen. On the other hand, tablet displays, especially when viewed in the landscape orientation, are best suited for showing multiple content panes at a time: the master on the left, and the detail to the right). Here, descendant navigation is usually implemented using a {@link android.app.FragmentTransaction} that adds, removes, or replaces the detail pane with new content.</p>
+
+<p>The basics of implementing this pattern are described in the <a href="{@docRoot}training/multiscreen/adaptui.html">Implementing Adaptive UI Flows</a> lesson of the <em>Designing for Multiple Screens</em> class. The class describes how to implement a master/detail flow using two activities on a handset and a single activity on a tablet.</p>
+
+<h2 id="external-activities">Navigate into External Activities</h2>
+
+<p>There are cases where descending into your application's information hierarchy leads to activities from other applications. For example, when viewing the contact details screen for an entry in the phone address book, a child screen detailing recent posts by the contact on a social network may belong to a social networking application.</p>
+
+<p>When launching another application's activity to allow the user to say, compose an email or pick a photo attachment, you generally don't want the user to return to this activity if they relaunch your application from the Launcher (the device home screen). It would be confusing if touching your application icon brought the user to a "compose email" screen.</p>
+
+<p>To prevent this from occurring, simply add the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} flag to the intent used to launch the external activity, like so:</p>
+
+<pre>
+Intent externalActivityIntent = new Intent(Intent.ACTION_PICK);
+externalActivityIntent.setType("image/*");
+externalActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+startActivity(externalActivityIntent);
+</pre>
diff --git a/docs/html/training/implementing-navigation/index.jd b/docs/html/training/implementing-navigation/index.jd
new file mode 100644
index 0000000..da61c81
--- /dev/null
+++ b/docs/html/training/implementing-navigation/index.jd
@@ -0,0 +1,68 @@
+page.title=Implementing Effective Navigation
+
+trainingnavtop=true
+startpage=true
+next.title=Implementing Lateral Navigation
+next.link=lateral.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+ <li>API level 14</li>
+ <li>Understanding of fragments and Android layouts</li>
+ <li><a href="{@docRoot}sdk/compatibility-library.html">The Android Support Package</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+</ul>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
+ <li><a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a></li>
+ <li><a href="{@docRoot}training/multiscreen/index.html">Designing for Multiple Screens</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+ class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p>This class demonstrates how to implement the key navigation design patterns detailed in the
+<a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a> class.
+The lessons in this class cover implementing navigation up, down, and across your application's <a
+href="{@docRoot}training/design-navigation/screen-planning.html#diagram- relationships">screen
+map</a>.</p>
+
+<p>After reading through the lessons in this class and exploring the associated sample application
+(see right), you should also have a basic understanding of how to use
+{@link android.app.ActionBar} and {@link android.support.v4.view.ViewPager}, two components that are fundamental to core app navigation.</p>
+
+
+<h2 id="lessons">Lessons</h2>
+
+
+<dl>
+ <dt><strong><a href="lateral.html">Implementing Lateral Navigation</a></strong></dt>
+ <dd>Learn how to implement tabs and horizontal paging (swipe views).</dd>
+
+ <dt><strong><a href="ancestral.html">Implementing Ancestral Navigation</a></strong></dt>
+ <dd>Learn how to implement <em>Up</em> navigation.</dd>
+
+ <dt><strong><a href="temporal.html">Implementing Temporal Navigation</a></strong></dt>
+ <dd>Learn how to correctly handle the <em>Back</em> button.</dd>
+
+ <dt><strong><a href="descendant.html">Implementing Descendant Navigation</a></strong></dt>
+ <dd>Learn the finer points of implementing navigation into your application's information hierarchy.</dd>
+</dl>
diff --git a/docs/html/training/implementing-navigation/lateral.jd b/docs/html/training/implementing-navigation/lateral.jd
new file mode 100644
index 0000000..d9ba5c9
--- /dev/null
+++ b/docs/html/training/implementing-navigation/lateral.jd
@@ -0,0 +1,252 @@
+page.title=Implementing Lateral Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Implementing Ancestral Navigation
+next.link=ancestral.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#tabs">Implement Tabs</a></li>
+ <li><a href="#horizontal-paging">Implement Horizontal Paging (Swipe Views)</a></li>
+ <li><a href="#swipe-tabs">Implement Swiping Between Tabs</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/design-navigation/descendant-lateral.html">Providing Descendant and Lateral Navigation</a></li>
+ <li><a href="{@docRoot}design/building-blocks/tabs.html">Android Design: Tabs</a></li>
+ <li><a href="{@docRoot}design/patterns/swipe-views.html">Android Design: Swipe Views</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/EffectiveNavigation.zip"
+ class="button">Download the sample app</a>
+<p class="filename">EffectiveNavigation.zip</p>
+</div>
+
+</div>
+</div>
+
+
+<p><em>Lateral navigation</em> is navigation between sibling screens in the application's screen hierarchy (sometimes referred to as a screen map). The most prominent lateral navigation patterns are tabs and horizontal paging (also known as swipe views). This pattern and others are described in <a href="{@docRoot}training/design-navigation/descendant-lateral.html">Designing Effective Navigation</a>. This lesson covers how to implement several of the primary lateral navigation patterns in Android.</p>
+
+<h2 id="tabs">Implement Tabs</h2>
+
+<p>Tabs allow the user to navigate between sibling screens by selecting the appropriate tab indicator available at the top of the display. In Android 3.0 and later, tabs are implemented using the {@link android.app.ActionBar} class, and are generally set up in {@link android.app.Activity#onCreate Activity.onCreate()}. In some cases, such as when horizontal space is limited and/or the number of tabs is large, an appropriate alternate presentation for tabs is a dropdown list (sometimes implemented using a {@link android.widget.Spinner}).</p>
+
+<p>In previous versions of Android, tabs could be implemented using a {@link android.widget.TabWidget} and {@link android.widget.TabHost}. For details, see the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Hello, Views</a> tutorial.</p>
+
+<p>As of Android 3.0, however, you should use either {@link android.app.ActionBar#NAVIGATION_MODE_TABS} or {@link android.app.ActionBar#NAVIGATION_MODE_LIST} along with the {@link android.app.ActionBar} class.</p>
+
+<h3>Implement the Tabs Pattern with NAVIGATION_MODE_TABS</h3>
+
+<p>To create tabs, you can use the following code in your activity's {@link android.app.Activity#onCreate onCreate()} method. Note that the exact presentation of tabs may vary per device and by the current device configuration, to make best use of available screen space. For example, Android may automatically collapse tabs into a dropdown list if tabs don't fit horizontally in the action bar.</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ ...
+ final ActionBar actionBar = getActionBar();
+
+ // Specify that tabs should be displayed in the action bar.
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ // Create a tab listener that is called when the user changes tabs.
+ ActionBar.TabListener tabListener = new ActionBar.TabListener() {
+ public void onTabSelected(ActionBar.Tab tab,
+ FragmentTransaction ft) { }
+
+ public void onTabUnselected(ActionBar.Tab tab,
+ FragmentTransaction ft) { }
+
+ public void onTabReselected(ActionBar.Tab tab,
+ FragmentTransaction ft) { }
+ };
+
+ // Add 3 tabs.
+ for (int i = 0; i < 3; i++) {
+ actionBar.addTab(
+ actionBar.newTab()
+ .setText("Tab " + (i + 1))
+ .setTabListener(tabListener));
+ }
+ ...
+}
+</pre>
+
+<h3>Implement the Tabs Pattern with NAVIGATION_MODE_LIST</h3>
+
+<p>To use a dropdown list instead, use the following code in your activity's {@link android.app.Activity#onCreate onCreate()} method. Dropdown lists are often preferable in cases where more information must be shown per navigation item, such as unread message counts, or where the number of available navigation items is large.</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ ...
+ final ActionBar actionBar = getActionBar();
+
+ // Specify that a dropdown list should be displayed in the action bar.
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+
+ actionBar.setListNavigationCallbacks(
+ // Specify a SpinnerAdapter to populate the dropdown list.
+ new ArrayAdapter<String>(
+ actionBar.getThemedContext(),
+ android.R.layout.simple_list_item_1,
+ android.R.id.text1,
+ new String[]{ "Tab 1", "Tab 2", "Tab 3" }),
+
+ // Provide a listener to be called when an item is selected.
+ new ActionBar.OnNavigationListener() {
+ public boolean onNavigationItemSelected(
+ int position, long id) {
+ // Take action here, e.g. switching to the
+ // corresponding fragment.
+ return true;
+ }
+ });
+ ...
+}
+</pre>
+
+<h2 id="horizontal-paging">Implement Horizontal Paging (Swipe Views)</h2>
+
+<p>Horizontal paging, or swipe views, allow users to <a href="{@docRoot}design/patterns/swipe-views">swipe</a> horizontally on the current screen to navigate to adjacent screens. This pattern can be implemented using the {@link android.support.v4.view.ViewPager} widget, currently available as part of the <a href="{@docRoot}sdk/compatibility-library.html">Android Support Package</a>. For navigating between sibling screens representing a fixed number of sections, it's best to provide the {@link android.support.v4.view.ViewPager} with a {@link android.support.v4.app.FragmentPagerAdapter}. For horizontal paging across collections of objects, it's best to use a {@link android.support.v4.app.FragmentStatePagerAdapter}, which destroys fragments as the user navigates to other pages, minimizing memory usage.</p>
+
+<p>Below is an example of using a {@link android.support.v4.view.ViewPager} to swipe across a collection of objects.</p>
+
+<pre>
+public class CollectionDemoActivity extends FragmentActivity {
+ // When requested, this adapter returns a DemoObjectFragment,
+ // representing an object in the collection.
+ DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
+ ViewPager mViewPager;
+
+ public void onCreate(Bundle savedInstanceState) {
+ // ViewPager and its adapters use support library
+ // fragments, so use getSupportFragmentManager.
+ mDemoCollectionPagerAdapter =
+ new DemoCollectionPagerAdapter(
+ getSupportFragmentManager());
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ mViewPager.setAdapter(mDemoCollectionPagerAdapter);
+ }
+}
+
+// Since this is an object collection, use a FragmentStatePagerAdapter,
+// and NOT a FragmentPagerAdapter.
+public class DemoCollectionPagerAdapter extends
+ FragmentStatePagerAdapter {
+ public DemoCollectionPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ {@literal @}Override
+ public Fragment getItem(int i) {
+ Fragment fragment = new DemoObjectFragment();
+ Bundle args = new Bundle();
+ // Our object is just an integer :-P
+ args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ {@literal @}Override
+ public int getCount() {
+ return 100;
+ }
+
+ {@literal @}Override
+ public CharSequence getPageTitle(int position) {
+ return "OBJECT " + (position + 1);
+ }
+}
+
+// Instances of this class are fragments representing a single
+// object in our collection.
+public static class DemoObjectFragment extends Fragment {
+ public static final String ARG_OBJECT = "object";
+
+ {@literal @}Override
+ public View onCreateView(LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ // The last two arguments ensure LayoutParams are inflated
+ // properly.
+ View rootView = inflater.inflate(
+ R.layout.fragment_collection_object, container, false);
+ Bundle args = getArguments();
+ ((TextView) rootView.findViewById(android.R.id.text1)).setText(
+ Integer.toString(args.getInt(ARG_OBJECT)));
+ return rootView;
+ }
+}
+</pre>
+
+<p>You can also add indicators to your horizontal paging UI by adding a {@link android.support.v4.view.PagerTitleStrip}. Below is an example layout XML file for an activity whose entire contents are a {@link android.support.v4.view.ViewPager} and a top-aligned {@link android.support.v4.view.PagerTitleStrip} inside it. Individual pages (provided by the adapter) occupy the remaining space inside the {@link android.support.v4.view.ViewPager}.</p>
+
+<pre>
+<android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.view.PagerTitleStrip
+ android:id="@+id/pager_title_strip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="#33b5e5"
+ android:textColor="#fff"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp" />
+
+</android.support.v4.view.ViewPager>
+</pre>
+
+<h2 id="swipe-tabs">Implement Swiping Between Tabs</h2>
+
+<p>One of the key design recommendations in Android 4.0 for tabs is to <a href="{@docRoot}design/patterns/swipe-views.html">allow swiping</a> between them where appropriate. This behavior enables users to swipe horizontally across the selected tab's contents to navigate to adjacent tabs, without needed to directly interact with the tabs themselves. To implement this, you can use a {@link android.support.v4.view.ViewPager} in conjunction with the {@link android.app.ActionBar} tabs API.</p>
+
+<p>Upon observing the current page changing, select the corresponding tab. You can set up this behavior using an {@link android.support.v4.view.ViewPager.OnPageChangeListener} in your activity's {@link android.app.Activity#onCreate onCreate()} method:</p>
+
+<pre>
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ ...
+ mViewPager.setOnPageChangeListener(
+ new ViewPager.SimpleOnPageChangeListener() {
+ {@literal @}Override
+ public void onPageSelected(int position) {
+ // When swiping between pages, select the
+ // corresponding tab.
+ getActionBar().setSelectedNavigationItem(position);
+ }
+ });
+ ...
+}
+</pre>
+
+<p>And upon selecting a tab, switch to the corresponding page in the {@link android.support.v4.view.ViewPager}. To do this, add an {@link android.app.ActionBar.TabListener} to your tab when creating it using the {@link android.app.ActionBar#newTab newTab()} method:</p>
+
+<pre>
+actionBar.newTab()
+ ...
+ .setTabListener(new ActionBar.TabListener() {
+ public void onTabSelected(ActionBar.Tab tab,
+ FragmentTransaction ft) {
+ // When the tab is selected, switch to the
+ // corresponding page in the ViewPager.
+ mViewPager.setCurrentItem(tab.getPosition());
+ }
+ ...
+ }));
+</pre>
diff --git a/docs/html/training/implementing-navigation/temporal.jd b/docs/html/training/implementing-navigation/temporal.jd
new file mode 100644
index 0000000..f36991f
--- /dev/null
+++ b/docs/html/training/implementing-navigation/temporal.jd
@@ -0,0 +1,83 @@
+page.title=Implementing Temporal Navigation
+parent.title=Implementing Effective Navigation
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Implementing Ancestral Navigation
+previous.link=ancestral.html
+next.title=Implementing Descendant Navigation
+next.link=descendant.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+<ol>
+ <li><a href="#back-fragments">Implement <em>Back</em> Navigation with Fragments</a></li>
+ <li><a href="#back-webviews">Implement <em>Back</em> Navigation with WebViews</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li>
+ <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a></li>
+ <li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li>
+</ul>
+
+</div>
+</div>
+
+
+<p><em>Temporal navigation</em> is navigation to previously visited screens. Users can visit previous screens by pressing the device <em>Back</em> button. This user interface pattern is described further in <a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a> in <em>Designing Effective Navigation</em> and in <a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a>.</p>
+
+<p>Android handles basic <em>Back</em> navigation for you (see <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a> for details on this behavior). This lesson discusses a number of cases where applications should provide specialized logic for the <em>Back</em> button.</p>
+
+
+<h2 id="back-fragments">Implement <em>Back</em> Navigation with Fragments</h2>
+
+<p>When using fragments in your application, individual {@link android.app.FragmentTransaction} objects can represent context changes that should be added to the back stack. For example, if you are implementing a <a href="descendant.html#master-detail">master/detail flow</a> on a handset by swapping out fragments (thus emulating a {@link android.app.Activity#startActivity startActivity()} call), you should ensure that pressing the <em>Back</em> button on a detail screen returns the user to the master screen. To do so, you can use {@link android.app.FragmentTransaction#addToBackStack addToBackStack()}:</p>
+
+<pre>
+// Works with either the framework FragmentManager or the
+// support package FragmentManager (getSupportFragmentManager).
+getFragmentManager().beginTransaction()
+ .add(detailFragment, "detail")
+
+ // Add this transaction to the back stack and commit.
+ .addToBackStack()
+ .commit();
+</pre>
+
+<p>The activity's {@link android.app.FragmentManager} handles <em>Back</em> button presses if there are {@link android.app.FragmentTransaction} objects on the back stack. When this happens, the {@link android.app.FragmentManager} pops the most recent transaction off the back stack and performs the reverse action (e.g., removing a fragment if the transaction added it).</p>
+
+<p>If your application updates other user interface elements to reflect the current state of your fragments, such as the action bar, remember to update the UI when you commit the transaction. You should update your user interface after the fragment manager back stack changes in addition to when you commit the transaction. You can listen for when a <code>FragmentTransaction</code> is reverted by setting up an {@link android.app.FragmentManager.OnBackStackChangedListener}:</p>
+
+<pre>
+getFragmentManager().addOnBackStackChangedListener(
+ new FragmentManager.OnBackStackChangedListener() {
+ public void onBackStackChanged() {
+ // Update your UI here.
+ }
+ });
+</pre>
+
+<h2 id="back-webviews">Implement <em>Back</em> Navigation with WebViews</h2>
+
+<p>If a part of your application is contained in a {@link android.webkit.WebView}, it may be appropriate for <em>Back</em> to traverse browser history. To do so, you can override {@link android.app.Activity#onBackPressed onBackPressed()} and proxy to the <code>WebView</code> if it has history state:</p>
+
+<pre>
+{@literal @}Override
+public void onBackPressed() {
+ if (mWebView.canGoBack()) {
+ mWebView.goBack();
+ return;
+ }
+
+ // Otherwise defer to system default behavior.
+ super.onBackPressed();
+}
+</pre>
+
+<p>Be careful when using this mechanism with highly dynamic web pages that can grow a large history. Pages that generate an extensive history, such as those that make frequent changes to the document hash, may make it tedious for users to get out of your activity.</p>
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
new file mode 100644
index 0000000..c570cd4
--- /dev/null
+++ b/graphics/java/android/graphics/Insets.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006 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.graphics;
+
+/**
+ * An Insets instance holds four integer offsets which describe changes to the four
+ * edges of a Rectangle. By convention, positive values move edges towards the
+ * centre of the rectangle.
+ * <p>
+ * Insets are immutable so may be treated as values.
+ *
+ * @hide
+ */
+public class Insets {
+ public static final Insets NONE = new Insets(0, 0, 0, 0);
+
+ public final int left;
+ public final int top;
+ public final int right;
+ public final int bottom;
+
+ private Insets(int left, int top, int right, int bottom) {
+ this.left = left;
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ }
+
+ // Factory methods
+
+ /**
+ * Return an Insets instance with the appropriate values.
+ *
+ * @param left the left inset
+ * @param top the top inset
+ * @param right the right inset
+ * @param bottom the bottom inset
+ *
+ * @return Insets instance with the appropriate values
+ */
+ public static Insets of(int left, int top, int right, int bottom) {
+ if (left == 0 && top == 0 && right == 0 && bottom == 0) {
+ return NONE;
+ }
+ return new Insets(left, top, right, bottom);
+ }
+
+ /**
+ * Return an Insets instance with the appropriate values.
+ *
+ * @param r the rectangle from which to take the values
+ *
+ * @return an Insets instance with the appropriate values
+ */
+ public static Insets of(Rect r) {
+ return of(r.left, r.top, r.right, r.bottom);
+ }
+
+ /**
+ * Two Insets instances are equal iff they belong to the same class and their fields are
+ * pairwise equal.
+ *
+ * @param o the object to compare this instance with.
+ *
+ * @return true iff this object is equal {@code o}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Insets insets = (Insets) o;
+
+ if (bottom != insets.bottom) return false;
+ if (left != insets.left) return false;
+ if (right != insets.right) return false;
+ if (top != insets.top) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = left;
+ result = 31 * result + top;
+ result = 31 * result + right;
+ result = 31 * result + bottom;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Insets{" +
+ "left=" + left +
+ ", top=" + top +
+ ", right=" + right +
+ ", bottom=" + bottom +
+ '}';
+ }
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 8cb8466..f68f9dc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -352,7 +352,7 @@
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
// ? HINTING_OFF : HINTING_ON);
mCompatScaling = mInvCompatScaling = 1;
- mLocale = Locale.getDefault();
+ setTextLocale(Locale.getDefault());
}
/**
@@ -365,7 +365,6 @@
public Paint(Paint paint) {
mNativePaint = native_initWithPaint(paint.mNativePaint);
setClassVariablesFrom(paint);
- mLocale = paint.mLocale;
}
/** Restores the paint to its default settings. */
@@ -379,7 +378,7 @@
mHasCompatScaling = false;
mCompatScaling = mInvCompatScaling = 1;
mBidiFlags = BIDI_DEFAULT_LTR;
- mLocale = Locale.getDefault();
+ setTextLocale(Locale.getDefault());
}
/**
@@ -1064,12 +1063,26 @@
/**
* Set the text locale.
*
- * This controls how the text will be drawn. Providing the ROOT Locale (default case)
- * means that the text will be drawn with the font corresponding to its script.
+ * The text locale affects how the text is drawn for some languages.
*
- * Using the CHINESE or CHINA Locale means that the text will be drawn with a Chinese font.
- * Using the JAPANESE or JAPAN Locale means that the text will be drawn with a Japanese font.
- * Using the KOREAN or KOREA Locale means that the text will be drawn with a Korean font.
+ * For example, if the locale is {@link Locale#CHINESE} or {@link Locale#CHINA},
+ * then the text renderer will prefer to draw text using a Chinese font. Likewise,
+ * if the locale is {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
+ * renderer will prefer to draw text using a Japanese font.
+ *
+ * This distinction is important because Chinese and Japanese text both use many
+ * of the same Unicode code points but their appearance is subtly different for
+ * each language.
+ *
+ * By default, the text locale is initialized to the system locale (as returned
+ * by {@link Locale#getDefault}). This assumes that the text to be rendered will
+ * most likely be in the user's preferred language.
+ *
+ * If the actual language of the text is known, then it can be provided to the
+ * text renderer using this method. The text renderer may attempt to guess the
+ * language script based on the contents of the text to be drawn independent of
+ * the text locale here. Specifying the text locale just helps it do a better
+ * job in certain ambiguous cases
*
* @param locale the paint's locale value for drawing text, must not be null.
*/
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index e101581..4beaecd 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -155,21 +155,23 @@
/**
* Update the texture image to the most recent frame from the image stream. This may only be
- * called while the OpenGL ES context that owns the texture is bound to the thread. It will
- * implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
+ * called while the OpenGL ES context that owns the texture is current on the calling thread.
+ * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
*/
public void updateTexImage() {
- int err = nativeUpdateTexImage();
- if (err != 0) {
- throw new RuntimeException("Error during updateTexImage (see logcat for details)");
- }
+ nativeUpdateTexImage();
}
/**
- * Detach the SurfaceTexture from the OpenGL ES context with which it is currently associated.
- * This can be used to change from one OpenGL ES context to another.
+ * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
+ * This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
+ * ES texture object will be deleted as a result of this call. After calling this method all
+ * calls to {@link #updateTexImage} will throw an {@link java.lang.IllegalStateException} until
+ * a successful call to {@link #attachToGLContext} is made.
*
- * @hide
+ * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
+ * contexts. Note, however, that the image contents are only accessible from one OpenGL ES
+ * context at a time.
*/
public void detachFromGLContext() {
int err = nativeDetachFromGLContext();
@@ -179,8 +181,17 @@
}
/**
+ * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread. A
+ * new OpenGL ES texture object is created and populated with the SurfaceTexture image frame
+ * that was current at the time of the last call to {@link #detachFromGLContext}. This new
+ * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
*
- * @hide
+ * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
+ * contexts. Note, however, that the image contents are only accessible from one OpenGL ES
+ * context at a time.
+ *
+ * @param texName The name of the OpenGL ES texture that will be created. This texture name
+ * must be unusued in the OpenGL ES context that is current on the calling thread.
*/
public void attachToGLContext(int texName) {
int err = nativeAttachToGLContext(texName);
@@ -292,7 +303,7 @@
private native void nativeGetTransformMatrix(float[] mtx);
private native long nativeGetTimestamp();
private native void nativeSetDefaultBufferSize(int width, int height);
- private native int nativeUpdateTexImage();
+ private native void nativeUpdateTexImage();
private native int nativeDetachFromGLContext();
private native int nativeAttachToGLContext(int texName);
private native int nativeGetQueuedCount();
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 86e824b..7d1942a 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -705,6 +705,20 @@
}
/**
+ * Return in insets the layout insets suggested by this Drawable for use with alignment
+ * operations during layout. Positive values move toward the
+ * center of the Drawable. Returns true if this drawable
+ * actually has a layout insets, else false. When false is returned, the padding
+ * is always set to 0.
+ *
+ * @hide
+ */
+ public boolean getLayoutInsets(Rect insets) {
+ insets.set(0, 0, 0, 0);
+ return false;
+ }
+
+ /**
* Make this drawable mutable. This operation cannot be reversed. A mutable
* drawable is guaranteed to not share its state with any other drawable.
* This is especially useful when you need to modify properties of drawables
@@ -965,9 +979,7 @@
Rect pad, Rect layoutBounds, String srcName) {
if (np != null) {
- NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName);
- npd.setLayoutBounds(layoutBounds);
- return npd;
+ return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
}
return new BitmapDrawable(res, bm);
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index b0f7fd3..e10f9e8 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -90,6 +90,18 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean getLayoutInsets(Rect insets) {
+ if (mCurrDrawable != null) {
+ return mCurrDrawable.getLayoutInsets(insets);
+ } else {
+ return super.getLayoutInsets(insets);
+ }
+ }
+
@Override
public void setAlpha(int alpha) {
if (mAlpha != alpha) {
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 1272071..e502b7a 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -16,9 +16,17 @@
package android.graphics.drawable;
-import android.graphics.*;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -29,7 +37,7 @@
import java.io.InputStream;
/**
- *
+ *
* A resizeable bitmap, with stretchable areas that you define. This type of image
* is defined in a .png file with a special format.
*
@@ -47,7 +55,6 @@
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
- private Rect mLayoutBounds;
private Paint mPaint;
private boolean mMutated;
@@ -56,7 +63,7 @@
// These are scaled to match the target density.
private int mBitmapWidth;
private int mBitmapHeight;
-
+
NinePatchDrawable() {
}
@@ -69,7 +76,7 @@
public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null);
}
-
+
/**
* Create drawable from raw nine-patch data, setting initial target density
* based on the display metrics of the resources.
@@ -79,7 +86,19 @@
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res);
mNinePatchState.mTargetDensity = mTargetDensity;
}
-
+
+ /**
+ * Create drawable from raw nine-patch data, setting initial target density
+ * based on the display metrics of the resources.
+ *
+ * @hide
+ */
+ public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
+ Rect padding, Rect layoutInsets, String srcName) {
+ this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, layoutInsets), res);
+ mNinePatchState.mTargetDensity = mTargetDensity;
+ }
+
/**
* Create drawable from existing nine-patch, not dealing with density.
* @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)}
@@ -99,13 +118,6 @@
mNinePatchState.mTargetDensity = mTargetDensity;
}
- /**
- * @hide
- */
- void setLayoutBounds(Rect layoutBounds) {
- mLayoutBounds = layoutBounds;
- }
-
private void setNinePatchState(NinePatchState state, Resources res) {
mNinePatchState = state;
mNinePatch = state.mNinePatch;
@@ -201,13 +213,26 @@
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations;
}
-
+
@Override
public boolean getPadding(Rect padding) {
padding.set(mPadding);
return true;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean getLayoutInsets(Rect insets) {
+ Rect layoutInsets = mNinePatchState.mLayoutInsets;
+ if (layoutInsets == null) {
+ return super.getLayoutInsets(insets);
+ }
+ insets.set(layoutInsets);
+ return true;
+ }
+
@Override
public void setAlpha(int alpha) {
if (mPaint == null && alpha == 0xFF) {
@@ -217,7 +242,7 @@
getPaint().setAlpha(alpha);
invalidateSelf();
}
-
+
@Override
public void setColorFilter(ColorFilter cf) {
if (mPaint == null && cf == null) {
@@ -267,6 +292,7 @@
options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
final Rect padding = new Rect();
+ final Rect layoutInsets = new Rect();
Bitmap bitmap = null;
try {
@@ -290,7 +316,7 @@
setNinePatchState(new NinePatchState(
new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"),
- padding, dither), r);
+ padding, layoutInsets, dither), r);
mNinePatchState.mTargetDensity = mTargetDensity;
a.recycle();
@@ -344,7 +370,7 @@
public Region getTransparentRegion() {
return mNinePatch.getTransparentRegion(getBounds());
}
-
+
@Override
public ConstantState getConstantState() {
mNinePatchState.mChangingConfigurations = getChangingConfigurations();
@@ -361,27 +387,36 @@
return this;
}
- final static class NinePatchState extends ConstantState {
+ private final static class NinePatchState extends ConstantState {
final NinePatch mNinePatch;
final Rect mPadding;
+ final Rect mLayoutInsets;
final boolean mDither;
int mChangingConfigurations;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
NinePatchState(NinePatch ninePatch, Rect padding) {
- this(ninePatch, padding, DEFAULT_DITHER);
+ this(ninePatch, padding, new Rect(), DEFAULT_DITHER);
}
- NinePatchState(NinePatch ninePatch, Rect rect, boolean dither) {
+ NinePatchState(NinePatch ninePatch, Rect padding, Rect layoutInsets) {
+ this(ninePatch, padding, layoutInsets, DEFAULT_DITHER);
+ }
+
+ NinePatchState(NinePatch ninePatch, Rect rect, Rect layoutInsets, boolean dither) {
mNinePatch = ninePatch;
mPadding = rect;
+ mLayoutInsets = layoutInsets;
mDither = dither;
}
+ // Copy constructor
+
NinePatchState(NinePatchState state) {
mNinePatch = new NinePatch(state.mNinePatch);
// Note we don't copy the padding because it is immutable.
mPadding = state.mPadding;
+ mLayoutInsets = state.mLayoutInsets;
mDither = state.mDither;
mChangingConfigurations = state.mChangingConfigurations;
mTargetDensity = state.mTargetDensity;
@@ -391,12 +426,12 @@
public Drawable newDrawable() {
return new NinePatchDrawable(this, null);
}
-
+
@Override
public Drawable newDrawable(Resources res) {
return new NinePatchDrawable(this, res);
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index c9554dc..38203af 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -66,9 +66,11 @@
float fuzz;
};
- void initialize(int32_t id, const String8& name, const String8& descriptor);
+ void initialize(int32_t id, int32_t generation,
+ const String8& name, const String8& descriptor);
inline int32_t getId() const { return mId; }
+ inline int32_t getGeneration() const { return mGeneration; }
inline const String8 getName() const { return mName; }
inline const String8 getDescriptor() const { return mDescriptor; }
inline uint32_t getSources() const { return mSources; }
@@ -91,17 +93,22 @@
return mKeyCharacterMap;
}
+ inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
+ inline bool hasVibrator() const { return mHasVibrator; }
+
inline const Vector<MotionRange>& getMotionRanges() const {
return mMotionRanges;
}
private:
int32_t mId;
+ int32_t mGeneration;
String8 mName;
String8 mDescriptor;
uint32_t mSources;
int32_t mKeyboardType;
sp<KeyCharacterMap> mKeyCharacterMap;
+ bool mHasVibrator;
Vector<MotionRange> mMotionRanges;
};
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index 698feb6..d6c49f7 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -127,26 +127,31 @@
// --- InputDeviceInfo ---
InputDeviceInfo::InputDeviceInfo() {
- initialize(-1, String8("uninitialized device info"), String8("unknown"));
+ initialize(-1, -1, String8("uninitialized device info"), String8("unknown"));
}
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
- mId(other.mId), mName(other.mName), mDescriptor(other.mDescriptor),
+ mId(other.mId), mGeneration(other.mGeneration),
+ mName(other.mName), mDescriptor(other.mDescriptor),
mSources(other.mSources),
mKeyboardType(other.mKeyboardType),
mKeyCharacterMap(other.mKeyCharacterMap),
+ mHasVibrator(other.mHasVibrator),
mMotionRanges(other.mMotionRanges) {
}
InputDeviceInfo::~InputDeviceInfo() {
}
-void InputDeviceInfo::initialize(int32_t id, const String8& name, const String8& descriptor) {
+void InputDeviceInfo::initialize(int32_t id, int32_t generation,
+ const String8& name, const String8& descriptor) {
mId = id;
+ mGeneration = generation;
mName = name;
mDescriptor = descriptor;
mSources = 0;
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ mHasVibrator = false;
mMotionRanges.clear();
}
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java
deleted file mode 100644
index 43e34fb..0000000
--- a/media/java/android/media/Crypto.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-/**
- * Crypto class can be used in conjunction with MediaCodec to decode
- * encrypted media data.
- * @hide
-*/
-public final class Crypto {
- public static final native boolean isCryptoSchemeSupported(byte[] uuid);
-
- public Crypto(byte[] uuid, byte[] initData) {
- native_setup(uuid, initData);
- }
-
- public final native boolean requiresSecureDecoderComponent(String mime);
-
- @Override
- protected void finalize() {
- native_finalize();
- }
-
- public native final void release();
- private static native final void native_init();
- private native final void native_setup(byte[] uuid, byte[] initData);
- private native final void native_finalize();
-
- static {
- System.loadLibrary("media_jni");
- native_init();
- }
-
- private int mNativeContext;
-}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 410383d..a65c2aa 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,7 +16,7 @@
package android.media;
-import android.media.Crypto;
+import android.media.MediaCrypto;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -24,7 +24,122 @@
/**
* MediaCodec class can be used to access low-level media codec, i.e.
* encoder/decoder components.
- * @hide
+ *
+ * <p>MediaCodec is generally used like this:
+ * <pre>
+ * MediaCodec codec = MediaCodec.createDecoderByType(type);
+ * codec.configure(format, ...);
+ * codec.start();
+ * ByteBuffer[] inputBuffers = codec.getInputBuffers();
+ * ByteBuffer[] outputBuffers = codec.getOutputBuffers();
+ * Map<String, Object> format = codec.getOutputFormat();
+ * for (;;) {
+ * int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
+ * if (inputBufferIndex >= 0) {
+ * // fill inputBuffers[inputBufferIndex] with valid data
+ * ...
+ * codec.queueInputBuffer(inputBufferIndex, ...);
+ * }
+ *
+ * int outputBufferIndex = codec.dequeueOutputBuffer(timeoutUs);
+ * if (outputBufferIndex >= 0) {
+ * // outputBuffer is ready to be processed or rendered.
+ * ...
+ * codec.releaseOutputBuffer(outputBufferIndex, ...);
+ * } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ * outputBuffers = codec.getOutputBuffers();
+ * } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ * // Subsequent data will conform to new format.
+ * format = codec.getOutputFormat();
+ * ...
+ * }
+ * }
+ * codec.stop();
+ * codec.release();
+ * codec = null;
+ * </pre>
+ *
+ * Each codec maintains a number of input and output buffers that are
+ * referred to by index in API calls.
+ * The contents of these buffers is represented by the ByteBuffer[] arrays
+ * accessible through getInputBuffers() and getOutputBuffers().
+ *
+ * After a successful call to {@link #start} the client "owns" neither
+ * input nor output buffers, subsequent calls to {@link #dequeueInputBuffer}
+ * and {@link #dequeueOutputBuffer} then transfer ownership from the codec
+ * to the client.<p>
+ * The client is not required to resubmit/release buffers immediately
+ * to the codec, the sample code above simply does this for simplicity's sake.<p>
+ * Once the client has an input buffer available it can fill it with data
+ * and submit it it to the codec via a call to {@link #queueInputBuffer}.<p>
+ * The codec in turn will return an output buffer to the client in response
+ * to {@link #dequeueOutputBuffer}. After the output buffer has been processed
+ * a call to {@link #releaseOutputBuffer} will return it to the codec.
+ * If a video surface has been provided in the call to {@link #configure},
+ * {@link #releaseOutputBuffer} optionally allows rendering of the buffer
+ * to the surface.<p>
+ *
+ * Input buffers (for decoders) and Output buffers (for encoders) contain
+ * encoded data according to the format's type. For video types this data
+ * is all the encoded data representing a single moment in time, for audio
+ * data this is slightly relaxed in that a buffer may contain multiple
+ * encoded frames of audio. In either case, buffers do not start and end on
+ * arbitrary byte boundaries, this is not a stream of bytes, it's a stream
+ * of access units.<p>
+ *
+ * Most formats also require the actual data to be prefixed by a number
+ * of buffers containing setup data, or codec specific data, i.e. the
+ * first few buffers submitted to the codec object after starting it must
+ * be codec specific data marked as such using the flag {@link #FLAG_CODECCONFIG}
+ * in a call to {@link #queueInputBuffer}.
+ *
+ * Once the client reaches the end of the input data it signals the end of
+ * the input stream by specifying a flag of {@link #FLAG_EOS} in the call to
+ * {@link #queueInputBuffer}. The codec will continue to return output buffers
+ * until it eventually signals the end of the output stream by specifying
+ * the same flag ({@link #FLAG_EOS}) on the BufferInfo returned in
+ * {@link #dequeueOutputBuffer}.
+ *
+ * In order to start decoding data that's not adjacent to previously submitted
+ * data (i.e. after a seek) it is necessary to {@link #flush} the decoder.
+ * Any input or output buffers the client may own at the point of the flush are
+ * immediately revoked, i.e. after a call to {@link #flush} the client does not
+ * own any buffers anymore.
+ * Note that the format of the data submitted after a flush must not change,
+ * flush does not support format discontinuities,
+ * for this a full stop(), configure(), start() cycle is necessary.
+ *
+ * The format of the media data is specified as string/value pairs represented
+ * as a Map<String, Object>.<p>
+ *
+ * Fields common to all formats:
+ *
+ * <table>
+ * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
+ * <tr><td>mime</td><td>String</td><td>The type of the format.</td></tr>
+ * <tr><td>max-input-size</td><td>Integer</td><td>optional, maximum size of a buffer of input data</td></tr>
+ * <tr><td>bitrate</td><td>Integer</td><td><b>encoder-only</b>, desired bitrate in bits/second</td></tr>
+ * </table>
+ *
+ * Video formats have the following fields:
+ * <table>
+ * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
+ * <tr><td>width</td><td>Integer</td><td></td></tr>
+ * <tr><td>height</td><td>Integer</td><td></td></tr>
+ * <tr><td>color-format</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>frame-rate</td><td>Integer or Float</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>i-frame-interval</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>stride</td><td>Integer</td><td><b>encoder-only</b>, optional, defaults to width</td></tr>
+ * <tr><td>slice-height</td><td>Integer</td><td><b>encoder-only</b>, optional, defaults to height</td></tr>
+ * </table>
+ *
+ * Audio formats have the following fields:
+ * <table>
+ * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
+ * <tr><td>channel-count</td><td>Integer</td><td></td></tr>
+ * <tr><td>sample-rate</td><td>Integer</td><td></td></tr>
+ * </table>
+ *
*/
final public class MediaCodec {
/** Per buffer metadata includes an offset and size specifying
@@ -32,43 +147,62 @@
*/
public final static class BufferInfo {
public void set(
- int offset, int size, long timeUs, int flags) {
- mOffset = offset;
- mSize = size;
- mPresentationTimeUs = timeUs;
- mFlags = flags;
+ int newOffset, int newSize, long newTimeUs, int newFlags) {
+ offset = newOffset;
+ size = newSize;
+ presentationTimeUs = newTimeUs;
+ flags = newFlags;
}
- public int mOffset;
- public int mSize;
- public long mPresentationTimeUs;
- public int mFlags;
+ public int offset;
+ public int size;
+ public long presentationTimeUs;
+ public int flags;
};
// The follow flag constants MUST stay in sync with their equivalents
// in MediaCodec.h !
- public static int FLAG_SYNCFRAME = 1;
- public static int FLAG_CODECCONFIG = 2;
- public static int FLAG_EOS = 4;
+
+ /** This indicates that the buffer marked as such contains the data
+ for a sync frame.
+ */
+ public static final int FLAG_SYNCFRAME = 1;
+
+ /** This indicated that the buffer marked as such contains codec
+ initialization / codec specific data instead of media data.
+ */
+ public static final int FLAG_CODECCONFIG = 2;
+
+ /** This signals the end of stream, i.e. no buffers will be available
+ after this, unless of course, {@link #flush} follows.
+ */
+ public static final int FLAG_EOS = 4;
// The following mode constants MUST stay in sync with their equivalents
// in media/hardware/CryptoAPI.h !
- public static int MODE_UNENCRYPTED = 0;
- public static int MODE_AES_CTR = 1;
+ public static final int MODE_UNENCRYPTED = 0;
+ public static final int MODE_AES_CTR = 1;
- /** Instantiate a codec component by mime type. For decoder components
- this is the mime type of media that this decoder should be able to
- decoder, for encoder components it's the type of media this encoder
- should encode _to_.
+ /** Instantiate a decoder supporting input data of the given mime type.
+ * @param type The mime type of the input data.
*/
- public static MediaCodec CreateByType(String type, boolean encoder) {
- return new MediaCodec(type, true /* nameIsType */, encoder);
+ public static MediaCodec createDecoderByType(String type) {
+ return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
+ }
+
+ /** Instantiate an encoder supporting output data of the given mime type.
+ * @param type The desired mime type of the output data.
+ */
+ public static MediaCodec createEncoderByType(String type) {
+ return new MediaCodec(type, true /* nameIsType */, true /* encoder */);
}
/** If you know the exact name of the component you want to instantiate
use this method to instantiate it. Use with caution.
+ Likely to be used with information obtained from {@link android.media.MediaCodecList}
+ @param name The name of the codec to be instantiated.
*/
- public static MediaCodec CreateByComponentName(String name) {
+ public static MediaCodec createByCodecName(String name) {
return new MediaCodec(
name, false /* nameIsType */, false /* unused */);
}
@@ -88,35 +222,14 @@
// to do this for you at some point in the future.
public native final void release();
+ /** If this codec is to be used as an encoder, pass this flag.
+ */
public static int CONFIGURE_FLAG_ENCODE = 1;
/** Configures a component.
- * @param format A map of string/value pairs describing the input format
- * (decoder) or the desired output format.
*
- * Video formats have the following fields:
- * "mime" - String
- * "width" - Integer
- * "height" - Integer
- * optional "max-input-size" - Integer
- *
- * Audio formats have the following fields:
- * "mime" - String
- * "channel-count" - Integer
- * "sample-rate" - Integer
- * optional "max-input-size" - Integer
- *
- * If the format is used to configure an encoder, additional
- * fields must be included:
- * "bitrate" - Integer (in bits/sec)
- *
- * for video formats:
- * "color-format" - Integer
- * "frame-rate" - Integer or Float
- * "i-frame-interval" - Integer
- * optional "stride" - Integer, defaults to "width"
- * optional "slice-height" - Integer, defaults to "height"
- *
+ * @param format The format of the input data (decoder) or the desired
+ * format of the output data (encoder).
* @param surface Specify a surface on which to render the output of this
* decoder.
* @param crypto Specify a crypto object to facilitate secure decryption
@@ -126,7 +239,7 @@
*/
public void configure(
Map<String, Object> format,
- Surface surface, Crypto crypto, int flags) {
+ Surface surface, MediaCrypto crypto, int flags) {
String[] keys = null;
Object[] values = null;
@@ -147,18 +260,23 @@
private native final void native_configure(
String[] keys, Object[] values,
- Surface surface, Crypto crypto, int flags);
+ Surface surface, MediaCrypto crypto, int flags);
/** After successfully configuring the component, call start. On return
* you can query the component for its input/output buffers.
*/
public native final void start();
+ /** Finish the decode/encode session, note that the codec instance
+ * remains active and ready to be {@link #start}ed again.
+ * To ensure that it is available to other client call {@link #release}
+ * and don't just rely on garbage collection to eventually do this for you.
+ */
public native final void stop();
/** Flush both input and output ports of the component, all indices
- * previously returned in calls to dequeueInputBuffer and
- * dequeueOutputBuffer become invalid.
+ * previously returned in calls to {@link #dequeueInputBuffer} and
+ * {@link #dequeueOutputBuffer} become invalid.
*/
public native final void flush();
@@ -169,24 +287,36 @@
* preceded by "codec specific data", i.e. setup data used to initialize
* the codec such as PPS/SPS in the case of AVC video or code tables
* in the case of vorbis audio.
- * The class MediaExtractor provides codec specific data as part of
+ * The class {@link android.media.MediaExtractor} provides codec
+ * specific data as part of
* the returned track format in entries named "csd-0", "csd-1" ...
*
* These buffers should be submitted using the flag {@link #FLAG_CODECCONFIG}.
*
* To indicate that this is the final piece of input data (or rather that
* no more input data follows unless the decoder is subsequently flushed)
- * specify the flag {@link FLAG_EOS}.
+ * specify the flag {@link #FLAG_EOS}.
+ *
+ * @param index The index of a client-owned input buffer previously returned
+ * in a call to {@link #dequeueInputBuffer}.
+ * @param offset The byte offset into the input buffer at which the data starts.
+ * @param size The number of bytes of valid input data.
+ * @param presentationTimeUs The time at which this buffer should be rendered.
+ * @param flags A bitmask of flags {@link #FLAG_SYNCFRAME},
+ * {@link #FLAG_CODECCONFIG} or {@link #FLAG_EOS}.
*/
public native final void queueInputBuffer(
int index,
int offset, int size, long presentationTimeUs, int flags);
- /** Similar to {@link queueInputBuffer} but submits a buffer that is
+ /** Similar to {@link #queueInputBuffer} but submits a buffer that is
* potentially encrypted. The buffer's data is considered to be
* partitioned into "subSamples", each subSample starts with a
* (potentially empty) run of plain, unencrypted bytes followed
* by a (also potentially empty) run of encrypted bytes.
+ * @param index The index of a client-owned input buffer previously returned
+ * in a call to {@link #dequeueInputBuffer}.
+ * @param offset The byte offset into the input buffer at which the data starts.
* @param numBytesOfClearData The number of leading unencrypted bytes in
* each subSample.
* @param numBytesOfEncryptedData The number of trailing encrypted bytes
@@ -212,27 +342,48 @@
long presentationTimeUs,
int flags);
- // Returns the index of an input buffer to be filled with valid data
- // or -1 if no such buffer is currently available.
- // This method will return immediately if timeoutUs == 0, wait indefinitely
- // for the availability of an input buffer if timeoutUs < 0 or wait up
- // to "timeoutUs" microseconds if timeoutUs > 0.
+ /** Returns the index of an input buffer to be filled with valid data
+ * or -1 if no such buffer is currently available.
+ * This method will return immediately if timeoutUs == 0, wait indefinitely
+ * for the availability of an input buffer if timeoutUs < 0 or wait up
+ * to "timeoutUs" microseconds if timeoutUs > 0.
+ * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite".
+ */
public native final int dequeueInputBuffer(long timeoutUs);
- // Returns the index of an output buffer that has been successfully
- // decoded or one of the INFO_* constants below.
- // The provided "info" will be filled with buffer meta data.
+ /** If a non-negative timeout had been specified in the call
+ * to {@link #dequeueOutputBuffer}, indicates that the call timed out.
+ */
public static final int INFO_TRY_AGAIN_LATER = -1;
+
+ /** The output format has changed, subsequent data will follow the new
+ * format. {@link #getOutputFormat} returns the new format.
+ */
public static final int INFO_OUTPUT_FORMAT_CHANGED = -2;
+
+ /** The output buffers have changed, the client must refer to the new
+ * set of output buffers returned by {@link #getOutputBuffers} from
+ * this point on.
+ */
public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3;
- /** Dequeue an output buffer, block at most "timeoutUs" microseconds. */
+ /** Dequeue an output buffer, block at most "timeoutUs" microseconds.
+ * Returns the index of an output buffer that has been successfully
+ * decoded or one of the INFO_* constants below.
+ * @param info Will be filled with buffer meta data.
+ * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite".
+ */
public native final int dequeueOutputBuffer(
BufferInfo info, long timeoutUs);
- // If you are done with a buffer, use this call to return the buffer to
- // the codec. If you previously specified a surface when configuring this
- // video decoder you can optionally render the buffer.
+ /** If you are done with a buffer, use this call to return the buffer to
+ * the codec. If you previously specified a surface when configuring this
+ * video decoder you can optionally render the buffer.
+ * @param index The index of a client-owned output buffer previously returned
+ * in a call to {@link #dequeueOutputBuffer}.
+ * @param render If a valid surface was specified when configuring the codec,
+ * passing true renders this output buffer to the surface.
+ */
public native final void releaseOutputBuffer(int index, boolean render);
/** Call this after dequeueOutputBuffer signals a format change by returning
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index b46ce96..1772e9c 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -20,22 +20,42 @@
* MediaCodecList class can be used to enumerate available codecs,
* find a codec supporting a given format and query the capabilities
* of a given codec.
- * @hide
*/
final public class MediaCodecList {
+ /** Count the number of available codecs.
+ */
public static native final int countCodecs();
+
+ /** Retrieve the codec name at the specified index. */
public static native final String getCodecName(int index);
+
+ /** Query if the codec at the specified index is an encoder. */
public static native final boolean isEncoder(int index);
+
+ /** Query the media types supported by the codec at the specified index */
public static native final String[] getSupportedTypes(int index);
public static final class CodecProfileLevel {
- public int mProfile;
- public int mLevel;
+ /** Defined in the OpenMAX IL specs, depending on the type of media
+ * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE
+ * or OMX_VIDEO_MPEG4PROFILETYPE.
+ */
+ public int profile;
+
+ /** Defined in the OpenMAX IL specs, depending on the type of media
+ * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE
+ * or OMX_VIDEO_MPEG4LEVELTYPE.
+ */
+ public int level;
};
public static final class CodecCapabilities {
- public CodecProfileLevel[] mProfileLevels;
- public int[] mColorFormats;
+ public CodecProfileLevel[] profileLevels;
+
+ /** Defined in the OpenMAX IL specs, color format values are drawn from
+ * OMX_COLOR_FORMATTYPE.
+ */
+ public int[] colorFormats;
};
public static native final CodecCapabilities getCodecCapabilities(
int index, String type);
diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java
new file mode 100644
index 0000000..0c7f6ef
--- /dev/null
+++ b/media/java/android/media/MediaCrypto.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * MediaCrypto class can be used in conjunction with {@link android.media.MediaCodec}
+ * to decode encrypted media data.
+ *
+ * Crypto schemes are assigned 16 byte UUIDs,
+ * the method {@link #isCryptoSchemeSupported} can be used to query if a given
+ * scheme is supported on the device.
+ *
+*/
+public final class MediaCrypto {
+ /** Query if the given scheme identified by its UUID is supported on
+ * this device.
+ * @param uuid The UUID of the crypto scheme.
+ */
+ public static final native boolean isCryptoSchemeSupported(byte[] uuid);
+
+ /** Instantiate a MediaCrypto object using opaque, crypto scheme specific
+ * data.
+ * @param uuid The UUID of the crypto scheme.
+ * @param initData Opaque initialization data specific to the crypto scheme.
+ */
+ public MediaCrypto(byte[] uuid, byte[] initData) {
+ native_setup(uuid, initData);
+ }
+
+ /** Query if the crypto scheme requires the use of a secure decoder
+ * to decode data of the given mime type.
+ * @param mime The mime type of the media data
+ */
+ public final native boolean requiresSecureDecoderComponent(String mime);
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ public native final void release();
+ private static native final void native_init();
+ private native final void native_setup(byte[] uuid, byte[] initData);
+ private native final void native_finalize();
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9c3b6a7..3f8b2ca 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -16,63 +16,227 @@
package android.media;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import java.io.FileDescriptor;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
/**
- * MediaExtractor
- * @hide
+ * MediaExtractor facilitates extraction of demuxed, typically encoded, media data
+ * from a data source.
+ * <p>It is generally used like this:
+ * <pre>
+ * MediaExtractor extractor = new MediaExtractor();
+ * extractor.setDataSource(...);
+ * int numTracks = extractor.countTracks();
+ * for (int i = 0; i < numTracks; ++i) {
+ * Map%lt;String, Object> format = extractor.getTrackFormat(i);
+ * String mime = (String)format.get("mime");
+ * if (weAreInterestedInThisTrack) {
+ * extractor.selectTrack(i);
+ * }
+ * }
+ * ByteBuffer inputBuffer = ByteBuffer.allocate(...)
+ * while (extractor.readSampleData(inputBuffer, ...) >= 0) {
+ * int trackIndex = extractor.getTrackIndex();
+ * long presentationTimeUs = extractor.getSampleTime();
+ * ...
+ * extractor.advance();
+ * }
+ *
+ * extractor.release();
+ * extractor = null;
+ * </pre>
*/
final public class MediaExtractor {
- public MediaExtractor(String path) {
- native_setup(path);
+ public MediaExtractor() {
+ native_setup();
}
+ /**
+ * Sets the data source as a content Uri.
+ *
+ * @param context the Context to use when resolving the Uri
+ * @param uri the Content URI of the data you want to extract from.
+ * @param headers the headers to be sent together with the request for the data
+ */
+ public final void setDataSource(
+ Context context, Uri uri, Map<String, String> headers)
+ throws IOException {
+ String scheme = uri.getScheme();
+ if(scheme == null || scheme.equals("file")) {
+ setDataSource(uri.getPath());
+ return;
+ }
+
+ AssetFileDescriptor fd = null;
+ try {
+ ContentResolver resolver = context.getContentResolver();
+ fd = resolver.openAssetFileDescriptor(uri, "r");
+ if (fd == null) {
+ return;
+ }
+ // Note: using getDeclaredLength so that our behavior is the same
+ // as previous versions when the content provider is returning
+ // a full file.
+ if (fd.getDeclaredLength() < 0) {
+ setDataSource(fd.getFileDescriptor());
+ } else {
+ setDataSource(
+ fd.getFileDescriptor(),
+ fd.getStartOffset(),
+ fd.getDeclaredLength());
+ }
+ return;
+ } catch (SecurityException ex) {
+ } catch (IOException ex) {
+ } finally {
+ if (fd != null) {
+ fd.close();
+ }
+ }
+
+ setDataSource(uri.toString(), headers);
+ }
+
+ /**
+ * Sets the data source (file-path or http URL) to use.
+ *
+ * @param path the path of the file, or the http URL
+ * @param headers the headers associated with the http request for the stream you want to play
+ */
+ public final void setDataSource(String path, Map<String, String> headers) {
+ String[] keys = null;
+ String[] values = null;
+
+ if (headers != null) {
+ keys = new String[headers.size()];
+ values = new String[headers.size()];
+
+ int i = 0;
+ for (Map.Entry<String, String> entry: headers.entrySet()) {
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ ++i;
+ }
+ }
+ setDataSource(path, keys, values);
+ }
+
+ private native final void setDataSource(
+ String path, String[] keys, String[] values);
+
+ /**
+ * Sets the data source (file-path or http URL) to use.
+ *
+ * @param path the path of the file, or the http URL of the stream
+ *
+ * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
+ * process other than the calling application. This implies that the pathname
+ * should be an absolute path (as any other process runs with unspecified current working
+ * directory), and that the pathname should reference a world-readable file.
+ * As an alternative, the application could first open the file for reading,
+ * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
+ */
+ public final void setDataSource(String path) {
+ setDataSource(path, null, null);
+ }
+
+ /**
+ * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
+ * to close the file descriptor. It is safe to do so as soon as this call returns.
+ *
+ * @param fd the FileDescriptor for the file you want to extract from.
+ */
+ public final void setDataSource(FileDescriptor fd) {
+ setDataSource(fd, 0, 0x7ffffffffffffffL);
+ }
+
+ /**
+ * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
+ * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
+ * to close the file descriptor. It is safe to do so as soon as this call returns.
+ *
+ * @param fd the FileDescriptor for the file you want to extract from.
+ * @param offset the offset into the file where the data to be extracted starts, in bytes
+ * @param length the length in bytes of the data to be extracted
+ */
+ public native final void setDataSource(
+ FileDescriptor fd, long offset, long length);
+
@Override
protected void finalize() {
native_finalize();
}
- // Make sure you call this when you're done to free up any resources
- // instead of relying on the garbage collector to do this for you at
- // some point in the future.
+ /** Make sure you call this when you're done to free up any resources
+ * instead of relying on the garbage collector to do this for you at
+ * some point in the future.
+ */
public native final void release();
+ /** Count the number of tracks found in the data source.
+ */
public native int countTracks();
+
+ /** Get the track format at the specified index.
+ * More detail on the representation can be found at {@link android.media.MediaCodec}
+ */
public native Map<String, Object> getTrackFormat(int index);
- // Subsequent calls to "readSampleData", "getSampleTrackIndex" and
- // "getSampleTime" only retrieve information for the subset of tracks
- // selected by the call below.
- // Selecting the same track multiple times has no effect, the track
- // is only selected once.
+ /** Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
+ * {@link #getSampleTime} only retrieve information for the subset of tracks
+ * selected by the call below.
+ * Selecting the same track multiple times has no effect, the track
+ * only selected once.
+ * Media data will be returned in the order of their timestamps.
+ */
public native void selectTrack(int index);
- // All selected tracks seek near the requested time. The next sample
- // returned for each selected track will be a sync sample.
+ /** All selected tracks seek near the requested time. The next sample
+ * returned for each selected track will be a sync sample.
+ */
public native void seekTo(long timeUs);
+ /** Advance to the next sample. Returns false if no more sample data
+ * is available (end of stream).
+ */
public native boolean advance();
- // Retrieve the current encoded sample and store it in the byte buffer
- // starting at the given offset. Returns the sample size.
+ /** Retrieve the current encoded sample and store it in the byte buffer
+ * starting at the given offset. Returns the sample size (or -1 if
+ * no more samples are available).
+ */
public native int readSampleData(ByteBuffer byteBuf, int offset);
- // Returns the track index the current sample originates from.
+ /** Returns the track index the current sample originates from (or -1
+ * if no more samples are available)
+ */
public native int getSampleTrackIndex();
- // Returns the current sample's presentation time in microseconds.
+ /** Returns the current sample's presentation time in microseconds.
+ * or -1 if no more samples are available.
+ */
public native long getSampleTime();
// Keep these in sync with their equivalents in NuMediaExtractor.h
+ /** The sample is a sync sample */
public static final int SAMPLE_FLAG_SYNC = 1;
+
+ /** The sample is (at least partially) encrypted, see also the documentation
+ * for {@link android.media.MediaCodec#queueSecureInputBuffer}
+ */
public static final int SAMPLE_FLAG_ENCRYPTED = 2;
- // Returns the current sample's flags.
+ /** Returns the current sample's flags. */
public native int getSampleFlags();
private static native final void native_init();
- private native final void native_setup(String path);
+ private native final void native_setup();
private native final void native_finalize();
static {
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index a3361d4..98e1bc5 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_Crypto.cpp \
+ android_media_MediaCrypto.cpp \
android_media_MediaCodec.cpp \
android_media_MediaCodecList.cpp \
android_media_MediaExtractor.cpp \
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 01d3833..979ffb0 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -20,7 +20,7 @@
#include "android_media_MediaCodec.h"
-#include "android_media_Crypto.h"
+#include "android_media_MediaCrypto.h"
#include "android_media_Utils.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
@@ -656,7 +656,7 @@
{ "native_configure",
"([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
- "Landroid/media/Crypto;I)V",
+ "Landroid/media/MediaCrypto;I)V",
(void *)android_media_MediaCodec_native_configure },
{ "start", "()V", (void *)android_media_MediaCodec_start },
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 2b8f91e..7139560 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -118,10 +118,10 @@
env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
jfieldID profileField =
- env->GetFieldID(profileLevelClazz, "mProfile", "I");
+ env->GetFieldID(profileLevelClazz, "profile", "I");
jfieldID levelField =
- env->GetFieldID(profileLevelClazz, "mLevel", "I");
+ env->GetFieldID(profileLevelClazz, "level", "I");
for (size_t i = 0; i < profileLevels.size(); ++i) {
const MediaCodecList::ProfileLevel &src = profileLevels.itemAt(i);
@@ -139,7 +139,7 @@
jfieldID profileLevelsField = env->GetFieldID(
capsClazz,
- "mProfileLevels",
+ "profileLevels",
"[Landroid/media/MediaCodecList$CodecProfileLevel;");
env->SetObjectField(caps, profileLevelsField, profileLevelArray);
@@ -155,7 +155,7 @@
}
jfieldID colorFormatsField = env->GetFieldID(
- capsClazz, "mColorFormats", "[I");
+ capsClazz, "colorFormats", "[I");
env->SetObjectField(caps, colorFormatsField, colorFormatsArray);
diff --git a/media/jni/android_media_Crypto.cpp b/media/jni/android_media_MediaCrypto.cpp
similarity index 80%
rename from media/jni/android_media_Crypto.cpp
rename to media/jni/android_media_MediaCrypto.cpp
index e1a60a1..b0ba307 100644
--- a/media/jni/android_media_Crypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -15,10 +15,10 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "Crypto-JNI"
+#define LOG_TAG "MediaCrypto-JNI"
#include <utils/Log.h>
-#include "android_media_Crypto.h"
+#include "android_media_MediaCrypto.h"
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
@@ -124,7 +124,7 @@
// static
sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
- jclass clazz = env->FindClass("android/media/Crypto");
+ jclass clazz = env->FindClass("android/media/MediaCrypto");
CHECK(clazz != NULL);
if (!env->IsInstanceOf(obj, clazz)) {
@@ -158,19 +158,19 @@
return old;
}
-static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
+static void android_media_MediaCrypto_release(JNIEnv *env, jobject thiz) {
setCrypto(env, thiz, NULL);
}
-static void android_media_Crypto_native_init(JNIEnv *env) {
- jclass clazz = env->FindClass("android/media/Crypto");
+static void android_media_MediaCrypto_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/MediaCrypto");
CHECK(clazz != NULL);
gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
CHECK(gFields.context != NULL);
}
-static void android_media_Crypto_native_setup(
+static void android_media_MediaCrypto_native_setup(
JNIEnv *env, jobject thiz,
jbyteArray uuidObj, jbyteArray initDataObj) {
jsize uuidLength = env->GetArrayLength(uuidObj);
@@ -186,16 +186,23 @@
jboolean isCopy;
jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
- jsize initDataLength = env->GetArrayLength(initDataObj);
- jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);
+ jsize initDataLength = 0;
+ jbyte *initData = NULL;
+
+ if (initDataObj != NULL) {
+ initDataLength = env->GetArrayLength(initDataObj);
+ initData = env->GetByteArrayElements(initDataObj, &isCopy);
+ }
sp<JCrypto> crypto = new JCrypto(
env, thiz, (const uint8_t *)uuid, initData, initDataLength);
status_t err = crypto->initCheck();
- env->ReleaseByteArrayElements(initDataObj, initData, 0);
- initData = NULL;
+ if (initDataObj != NULL) {
+ env->ReleaseByteArrayElements(initDataObj, initData, 0);
+ initData = NULL;
+ }
env->ReleaseByteArrayElements(uuidObj, uuid, 0);
uuid = NULL;
@@ -211,12 +218,12 @@
setCrypto(env,thiz, crypto);
}
-static void android_media_Crypto_native_finalize(
+static void android_media_MediaCrypto_native_finalize(
JNIEnv *env, jobject thiz) {
- android_media_Crypto_release(env, thiz);
+ android_media_MediaCrypto_release(env, thiz);
}
-static jboolean android_media_Crypto_isCryptoSchemeSupported(
+static jboolean android_media_MediaCrypto_isCryptoSchemeSupported(
JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
jsize uuidLength = env->GetArrayLength(uuidObj);
@@ -239,7 +246,7 @@
return result;
}
-static jboolean android_media_Crypto_requiresSecureDecoderComponent(
+static jboolean android_media_MediaCrypto_requiresSecureDecoderComponent(
JNIEnv *env, jobject thiz, jstring mimeObj) {
if (mimeObj == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -268,24 +275,24 @@
}
static JNINativeMethod gMethods[] = {
- { "release", "()V", (void *)android_media_Crypto_release },
- { "native_init", "()V", (void *)android_media_Crypto_native_init },
+ { "release", "()V", (void *)android_media_MediaCrypto_release },
+ { "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
{ "native_setup", "([B[B)V",
- (void *)android_media_Crypto_native_setup },
+ (void *)android_media_MediaCrypto_native_setup },
{ "native_finalize", "()V",
- (void *)android_media_Crypto_native_finalize },
+ (void *)android_media_MediaCrypto_native_finalize },
{ "isCryptoSchemeSupported", "([B)Z",
- (void *)android_media_Crypto_isCryptoSchemeSupported },
+ (void *)android_media_MediaCrypto_isCryptoSchemeSupported },
{ "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
- (void *)android_media_Crypto_requiresSecureDecoderComponent },
+ (void *)android_media_MediaCrypto_requiresSecureDecoderComponent },
};
int register_android_media_Crypto(JNIEnv *env) {
return AndroidRuntime::registerNativeMethods(env,
- "android/media/Crypto", gMethods, NELEM(gMethods));
+ "android/media/MediaCrypto", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_Crypto.h b/media/jni/android_media_MediaCrypto.h
similarity index 100%
rename from media/jni/android_media_Crypto.h
rename to media/jni/android_media_MediaCrypto.h
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 8c661b7..9883962 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -63,8 +63,13 @@
mClass = NULL;
}
-status_t JMediaExtractor::setDataSource(const char *path) {
- return mImpl->setDataSource(path);
+status_t JMediaExtractor::setDataSource(
+ const char *path, const KeyedVector<String8, String8> *headers) {
+ return mImpl->setDataSource(path, headers);
+}
+
+status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
+ return mImpl->setDataSource(fd, offset, size);
}
size_t JMediaExtractor::countTracks() const {
@@ -200,7 +205,7 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
+ return -1;
}
return extractor->countTracks();
@@ -380,24 +385,42 @@
}
static void android_media_MediaExtractor_native_setup(
- JNIEnv *env, jobject thiz, jstring path) {
+ JNIEnv *env, jobject thiz) {
sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
+ setMediaExtractor(env,thiz, extractor);
+}
- if (path == NULL) {
+static void android_media_MediaExtractor_setDataSource(
+ JNIEnv *env, jobject thiz,
+ jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ if (pathObj == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
- const char *tmp = env->GetStringUTFChars(path, NULL);
-
- if (tmp == NULL) {
+ KeyedVector<String8, String8> headers;
+ if (!ConvertKeyValueArraysToKeyedVector(
+ env, keysArray, valuesArray, &headers)) {
return;
}
- status_t err = extractor->setDataSource(tmp);
+ const char *path = env->GetStringUTFChars(pathObj, NULL);
- env->ReleaseStringUTFChars(path, tmp);
- tmp = NULL;
+ if (path == NULL) {
+ return;
+ }
+
+ status_t err = extractor->setDataSource(path, &headers);
+
+ env->ReleaseStringUTFChars(pathObj, path);
+ path = NULL;
if (err != OK) {
jniThrowException(
@@ -406,8 +429,34 @@
"Failed to instantiate extractor.");
return;
}
+}
- setMediaExtractor(env,thiz, extractor);
+static void android_media_MediaExtractor_setDataSourceFd(
+ JNIEnv *env, jobject thiz,
+ jobject fileDescObj, jlong offset, jlong length) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ if (fileDescObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
+
+ status_t err = extractor->setDataSource(fd, offset, length);
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate extractor.");
+ return;
+ }
}
static void android_media_MediaExtractor_native_finalize(
@@ -443,11 +492,18 @@
{ "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
- { "native_setup", "(Ljava/lang/String;)V",
+ { "native_setup", "()V",
(void *)android_media_MediaExtractor_native_setup },
{ "native_finalize", "()V",
(void *)android_media_MediaExtractor_native_finalize },
+
+ { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;"
+ "[Ljava/lang/String;)V",
+ (void *)android_media_MediaExtractor_setDataSource },
+
+ { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
+ (void *)android_media_MediaExtractor_setDataSourceFd },
};
int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 49a64d6..1aacea2 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -19,7 +19,9 @@
#include <media/stagefright/foundation/ABase.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include "jni.h"
@@ -30,7 +32,11 @@
struct JMediaExtractor : public RefBase {
JMediaExtractor(JNIEnv *env, jobject thiz);
- status_t setDataSource(const char *path);
+ status_t setDataSource(
+ const char *path,
+ const KeyedVector<String8, String8> *headers);
+
+ status_t setDataSource(int fd, off64_t offset, off64_t size);
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 1190448..a4d88ff 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -85,6 +85,16 @@
return env->NewObject(clazz, integerConstructID, value);
}
+static jobject makeLongObject(JNIEnv *env, int64_t value) {
+ jclass clazz = env->FindClass("java/lang/Long");
+ CHECK(clazz != NULL);
+
+ jmethodID longConstructID = env->GetMethodID(clazz, "<init>", "(J)V");
+ CHECK(longConstructID != NULL);
+
+ return env->NewObject(clazz, longConstructID, value);
+}
+
static jobject makeFloatObject(JNIEnv *env, float value) {
jclass clazz = env->FindClass("java/lang/Float");
CHECK(clazz != NULL);
@@ -158,6 +168,15 @@
break;
}
+ case AMessage::kTypeInt64:
+ {
+ int64_t val;
+ CHECK(msg->findInt64(key, &val));
+
+ valueObj = makeLongObject(env, val);
+ break;
+ }
+
case AMessage::kTypeFloat:
{
float val;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 2a4d59b..8acbae3 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -1298,7 +1298,9 @@
}
// Update the pause state.
+ boolean pausing = false;
if (mPaused != mRequestPaused) {
+ pausing = mRequestPaused;
mPaused = mRequestPaused;
sGLThreadManager.notifyAll();
if (LOG_PAUSE_RESUME) {
@@ -1324,12 +1326,16 @@
lostEglContext = false;
}
- // Do we need to release the EGL surface?
- if (mHaveEglSurface && mPaused) {
+ // When pausing, release the EGL surface:
+ if (pausing && mHaveEglSurface) {
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
}
stopEglSurfaceLocked();
+ }
+
+ // When pausing, optionally release the EGL Context:
+ if (pausing && mHaveEglContext) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
boolean preserveEglContextOnPause = view == null ?
false : view.mPreserveEGLContextOnPause;
@@ -1339,6 +1345,10 @@
Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
}
}
+ }
+
+ // When pausing, optionally terminate EGL:
+ if (pausing) {
if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
mEglHelper.finish();
if (LOG_SURFACE) {
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml
index f3feee8..4fb26ae 100644
--- a/packages/BackupRestoreConfirmation/AndroidManifest.xml
+++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml
@@ -19,6 +19,7 @@
package="com.android.backupconfirm" >
<uses-permission android:name="android.permission.BACKUP" />
+ <uses-permission android:name="android.permission.CRYPT_KEEPER" />
<application android:allowClearUserData="false"
android:allowBackup="false"
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index 7f1d059..82ac8cb 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -265,6 +265,7 @@
} catch (Exception e) {
// If we can't talk to the mount service we have a serious problem; fail
// "secure" i.e. assuming that the device is encrypted.
+ Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
return true;
}
}
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 56826fa..1654eca 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Kennisgewings"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-verbind"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Stel invoer metodes op"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fisiese sleutelbord"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Laat die program <xliff:g id="APPLICATION">%1$s</xliff:g> toe om toegang tot die USB-toestel te kry?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Laat die program <xliff:g id="APPLICATION">%1$s</xliff:g> toe om toegang tot die USB-toebehoorsel te kry?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Maak <xliff:g id="ACTIVITY">%1$s</xliff:g> oop wanneer hierdie USB-toestel gekoppel is?"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7013862..42eef13 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"ማሳወቂያዎች"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"ብሉቱዝ አያይዝ"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"የግቤት ስልቶችን አዘጋጅ"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"የሚዳሰስ የቁልፍ ሰሌዳ"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"መተግበሪያ <xliff:g id="APPLICATION">%1$s</xliff:g> የUSB መሣሪያን ለመድረስ ይፍቀድ?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"መተግበሪያ <xliff:g id="APPLICATION">%1$s</xliff:g> የUSB ተቀጥላ ላይ እንዲደርስ ፍቀድ?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"የዚህ USB ተቀጥላ ሲያያዝ <xliff:g id="ACTIVITY">%1$s</xliff:g>ይከፈት?"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 201f72c..9d6051e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"التنبيهات"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"تم إنشاء الاتصال بالإنترنت عن طريق البلوتوث."</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"إعداد أسلوب الإدخال"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"لوحة مفاتيح فعلية"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى جهاز USB؟"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى ملحق USB؟"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل جهاز USB هذا؟"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index b885503..433c124 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Паведамленні"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Прывязаныя праз Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Налада метадаў уводу"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Фізічная клавіятура"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Дазволіць праыкладанню <xliff:g id="APPLICATION">%1$s</xliff:g> атрымлiваць доступ да прылады USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Дазволіць прыкладанню <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да прылады USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Адкрыць <xliff:g id="ACTIVITY">%1$s</xliff:g>, калі гэтая USB-прылада падлучаная?"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 0ebced0..427abce 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificacions"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth sense fil"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura els mètodes d\'entrada"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclat físic"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Vols permetre que l\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi al dispositiu USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vols permetre que l\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a l\'accessori USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vols que s\'obri <xliff:g id="ACTIVITY">%1$s</xliff:g> quan aquest dispositiu USB estigui connectat?"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ac58b061..6c614cd 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Underretninger"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-tethering anvendt"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurer inputmetoder"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysisk tastatur"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Tillad, at appen <xliff:g id="APPLICATION">%1$s</xliff:g> kan få adgang til USB-enheden?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vil du tillade, at appen <xliff:g id="APPLICATION">%1$s</xliff:g> får adgang til USB-enheden?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vil du åbne <xliff:g id="ACTIVITY">%1$s</xliff:g>, når denne USB-enhed er tilsluttet?"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 39d5c33..e3d82b1 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Benachrichtigungen"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-Tethering aktiv"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Eingabemethoden einrichten"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physische Tastatur"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"App <xliff:g id="APPLICATION">%1$s</xliff:g> Zugriff auf USB-Gerät gewähren?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"App <xliff:g id="APPLICATION">%1$s</xliff:g> Zugriff auf USB-Zubehör gewähren?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"<xliff:g id="ACTIVITY">%1$s</xliff:g> öffnen, wenn dieses USB-Gerät verbunden ist?"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 48158db..5511b1a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Ειδοποιήσεις"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Έγινε σύνδεση μέσω Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Ρύθμιση μεθόδων εισαγωγής"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Φυσικό πληκτρολόγιο"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Να επιτρέπεται στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> η πρόσβαση στη συσκευή USB;"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Να επιτρέπεται στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> η πρόσβαση στο αξεσουάρ USB;"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Άνοιγμα του <xliff:g id="ACTIVITY">%1$s</xliff:g> κατά τη σύνδεση αυτής της συσκευής USB;"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 148924a..b20a5f3 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifications"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4db7130..96c8c23 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificaciones"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth anclado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de intro."</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"¿Deseas que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al dispositivo USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"¿Deseas que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al accesorio USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"¿Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> cuando este dispositivo USB esté conectado?"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index f7f73d7..0b3e4e4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificaciones"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth anclado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de introducción"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"¿Permitir que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al dispositivo USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"¿Permitir que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al accesorio USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"¿Quieres abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> al conectar este dispositivo USB?"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 8e3a1e3..055e3ee 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Teatised"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth on jagatud"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Seadista sisestusmeetodeid"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Füüsiline klaviatuur"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Kas lubate rakendusel <xliff:g id="APPLICATION">%1$s</xliff:g> USB-seadmele juurde pääseda?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Kas lubate rakendusel <xliff:g id="APPLICATION">%1$s</xliff:g> USB-seadmele juurde pääseda?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Kas avada <xliff:g id="ACTIVITY">%1$s</xliff:g>, kui see USB-seade on ühendatud?"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 569e929..df25d5e 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"اعلان ها"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"اتصال اینترنتی با بلوتوث تلفن همراه"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"تنظیم روشهای ورودی"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"صفحه کلید فیزیکی"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"به برنامه <xliff:g id="APPLICATION">%1$s</xliff:g> اجازه می دهید به دستگاه USB دسترسی داشته باشد؟"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"به برنامه <xliff:g id="APPLICATION">%1$s</xliff:g> اجازه میدهد تا به وسیله جانبی USB دسترسی داشته باشد؟"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"وقتی این دستگاه USB وصل است، <xliff:g id="ACTIVITY">%1$s</xliff:g> باز شود؟"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 93e6b62..370d234 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Ilmoitukset"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth yhdistetty"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Määritä syöttötavat"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyysinen näppäimistö"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Annetaanko sovellukselle <xliff:g id="APPLICATION">%1$s</xliff:g> lupa käyttää USB-laitetta?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Annetaanko sovellukselle <xliff:g id="APPLICATION">%1$s</xliff:g> lupa käyttää USB-lisälaitetta?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Avataanko <xliff:g id="ACTIVITY">%1$s</xliff:g> tämän USB-laitteen ollessa kytkettynä?"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1a8c6a7..c7f307c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifications"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Connexion Bluetooth partagée"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder au périphérique USB ?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à l\'accessoire USB ?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Ouvrir <xliff:g id="ACTIVITY">%1$s</xliff:g> lors de la connexion de ce périphérique USB ?"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 741f1b4..9805f90 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"सूचनाएं"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth टीदर किया गया"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट पद्धति सेट करें"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"भौतिक कीबोर्ड"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"एप्लिकेशन <xliff:g id="APPLICATION">%1$s</xliff:g> को USB उपकरण तक पहुंचने दें?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"एप्लिकेशन <xliff:g id="APPLICATION">%1$s</xliff:g> को USB सहायक उपकरण तक पहुंचने दें?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"जब यह USB उपकरण कनेक्ट किया जाए, तब <xliff:g id="ACTIVITY">%1$s</xliff:g> को खोलें?"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 1c28b57..87a9d45 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Értesítések"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth megosztva"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Beviteli módok beállítása"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizikai billentyűzet"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazás hozzáférhet az USB-eszközhöz?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazás hozzáférhet az USB-kiegészítőhöz?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"<xliff:g id="ACTIVITY">%1$s</xliff:g> megnyitása, ha USB-kiegészítő csatlakoztatva van?"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index cdf7c9d..a725377 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notifiche"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth con tethering"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura metodi di immissione"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastiera fisica"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Consentire all\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere al dispositivo USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Consentire all\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere all\'accessorio USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Aprire <xliff:g id="ACTIVITY">%1$s</xliff:g> quando questo dispositivo USB è collegato?"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index bc5e873..03fba7e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"התראות"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth קשור"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"הגדר שיטות קלט"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"מקלדת פיזית"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"לאפשר ליישום <xliff:g id="APPLICATION">%1$s</xliff:g> גישה להתקן ה-USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"לאפשר ליישום <xliff:g id="APPLICATION">%1$s</xliff:g> גישה לאביזר ה-USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"האם לפתוח את <xliff:g id="ACTIVITY">%1$s</xliff:g> כאשר מכשיר USB זה מחובר?"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 01d76eb..4818ca9 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"通知"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetoothテザリング接続"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"入力方法をセットアップ"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"物理キーボード"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"アプリ「<xliff:g id="APPLICATION">%1$s</xliff:g>」にUSBデバイスへのアクセスを許可しますか?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"アプリ「<xliff:g id="APPLICATION">%1$s</xliff:g>」にUSBアクセサリへのアクセスを許可しますか?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"このUSBデバイスが接続されたときに<xliff:g id="ACTIVITY">%1$s</xliff:g>を開きますか?"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 88194df..3321912 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"알림"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"블루투스 테더링됨"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"입력 방법 설정"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"물리적 키보드"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱이 USB 기기에 액세스하도록 허용하시겠습니까?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱이 USB 액세서리에 액세스하도록 허용하시겠습니까?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"USB 기기가 연결될 때 <xliff:g id="ACTIVITY">%1$s</xliff:g>(을)를 여시겠습니까?"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 942e159..831fa41 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Pranešimai"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"„Bluetooth“ susieta"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nustatyti įvesties metodus"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizinė klaviatūra"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Leisti programai „<xliff:g id="APPLICATION">%1$s</xliff:g>“ pasiekti USB įrenginį?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Leisti programai „<xliff:g id="APPLICATION">%1$s</xliff:g>“ pasiekti USB priedą?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Atidaryti <xliff:g id="ACTIVITY">%1$s</xliff:g>, kai prijungtas šis USB įrenginys?"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index b5c9130..158c850 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Paziņojumi"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth piesaiste"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Iestatīt ievades metodes"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fiziskā tastatūra"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Vai ļaut lietotnei <xliff:g id="APPLICATION">%1$s</xliff:g> piekļūt šai USB ierīcei?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vai ļaut lietotnei <xliff:g id="APPLICATION">%1$s</xliff:g> piekļūt šim USB piederumam?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vai atvērt darbību <xliff:g id="ACTIVITY">%1$s</xliff:g>, kad tiek pievienota šī USB ierīce?"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index f08013d..899a204 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Pemberitahuan"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth ditambatkan"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Sediakan kaedah input"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Papan kekunci fizikal"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Benarkan aplikasi <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses peranti USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Benarkan aplikasi <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses aksesori USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Buka <xliff:g id="ACTIVITY">%1$s</xliff:g> apabila peranti USB ini disambungkan?"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5314ba3..e05e2c9 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Meldingen"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth getetherd"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Invoermethoden instellen"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysiek toetsenbord"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"De app <xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot het USB-apparaat?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"De app <xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot het USB-accessoire?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"<xliff:g id="ACTIVITY">%1$s</xliff:g> openen wanneer dit USB-apparaat wordt aangesloten?"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 26e3989..0618eb5 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Powiadomienia"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth – podłączono"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfiguruj metody wprowadzania"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Klawiatura fizyczna"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na dostęp do urządzenia USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na dostęp do urządzenia USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Czy otworzyć <xliff:g id="ACTIVITY">%1$s</xliff:g> po podłączeniu tego urządzenia USB?"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a5999e3..6a278ba 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificações"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth ligado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos introdução"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Permitir que a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> aceda ao dispositivo USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permitir que a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> aceda ao acessório USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este dispositivo USB estiver ligado?"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 02bef77..23a2ba5 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificações"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth vinculado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Permitir que o aplicativo <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o dispositivo USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permitir que o aplicativo <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o acessório USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este dispositivo USB estiver conectado?"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 4057599..3753309 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Уведомления"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Общий модем доступен через Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Настройка способов ввода"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физическая клавиатура"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Открыть приложению \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ к USB-устройству?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Открыть приложению \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ к USB-устройству?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Запускать <xliff:g id="ACTIVITY">%1$s</xliff:g> при подключении этого USB-устройства?"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 67368ac..599f99c 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Upozornenia"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Zdieľané dátové pripojenie cez Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nastavenie metód vstupu"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyzická klávesnica"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> prístup k zariadeniu USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> prístup k periférnemu zariadeniu USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Chcete pri pripojení tohto zariadenia USB otvoriť aplikáciu <xliff:g id="ACTIVITY">%1$s</xliff:g>?"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 77aef3d..fb229e2 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Obvestila"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Internetna povezava prek Bluetootha"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nastavi načine vnosa"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizična tipkovnica"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Želite programu <xliff:g id="APPLICATION">%1$s</xliff:g> dovoliti dostop do naprave USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Želite dovoliti programu <xliff:g id="APPLICATION">%1$s</xliff:g> dostop do dodatka USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Želite, da se odpre <xliff:g id="ACTIVITY">%1$s</xliff:g>, ko priključite to napravo USB?"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index ba4edea..8eaa126 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Aviseringar"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Internetdelning via Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurera inmatningsmetoder"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysiskt tangentbord"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Vill du tillåta att appen <xliff:g id="APPLICATION">%1$s</xliff:g> använder USB-enheten?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vill du tillåta att appen <xliff:g id="APPLICATION">%1$s</xliff:g> använder USB-tillbehöret?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vill du öppna <xliff:g id="ACTIVITY">%1$s</xliff:g> när den här USB-enheten ansluts?"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8a6b050..19bd4ab 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -46,8 +46,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Arifa"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth imefungwa"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Weka mbinu za ingizo"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Kibodi halisi"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Ruhusu programu <xliff:g id="APPLICATION">%1$s</xliff:g> kufikia kifaa cha USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Ruhusu programu <xliff:g id="APPLICATION">%1$s</xliff:g> kufikia kifaa cha ziada cha USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Je, ungetaka kufungua <xliff:g id="ACTIVITY">%1$s</xliff:g>wakati kifaa cha USB kimeunganishwa?"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 55ecfae..2054121e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Mga Notification"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Na-tether ang bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"I-set up paraan ng pag-input"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Aktwal na keyboard"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Payagan ang app na <xliff:g id="APPLICATION">%1$s</xliff:g> na i-access ang USB device?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Payagan ang app na <xliff:g id="APPLICATION">%1$s</xliff:g> na i-access ang USB accessory?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Buksan ang <xliff:g id="ACTIVITY">%1$s</xliff:g> kapag nakakonekta ang USB device na ito?"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f0aa516..ca422f7 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Bildirimler"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth paylaşımı tamam"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Giriş yöntemlerini ayarla"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fiziksel klavye"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasının USB cihazına erişmesine izin verilsin mi?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasının USB aksesuarına erişmesine izin verilsin mi?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Bu USB cihaz bağlandığında <xliff:g id="ACTIVITY">%1$s</xliff:g> açılsın mı?"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index abd024a..05ab87c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Thông báo"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth được dùng làm điểm truy cập Internet"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Thiết lập phương thức nhập"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Bàn phím thực"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Cho phép ứng dụng <xliff:g id="APPLICATION">%1$s</xliff:g> truy cập thiết bị USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Cho phép ứng dụng <xliff:g id="APPLICATION">%1$s</xliff:g> truy cập phụ kiện USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Mở <xliff:g id="ACTIVITY">%1$s</xliff:g> khi thiết bị USB này được kết nối?"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ec92713c..c862d74 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"通知"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"已通过蓝牙共享网络"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"设置输入法"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"物理键盘"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"允许应用“<xliff:g id="APPLICATION">%1$s</xliff:g>”访问该 USB 设备吗?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"允许应用“<xliff:g id="APPLICATION">%1$s</xliff:g>”访问该 USB 配件吗?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"要在连接此 USB 设备时打开<xliff:g id="ACTIVITY">%1$s</xliff:g>吗?"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 27dd8fd..91f6566 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -48,8 +48,7 @@
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Izaziso"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Ukusebenzisa i-Bluetooth njengemodemu"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Izilungiselelo zezindlela zokufakwayo"</string>
- <!-- no translation found for status_bar_use_physical_keyboard (7551903084416057810) -->
- <skip />
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Ukwakheka kwekhibhodi"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Vumela insiza <xliff:g id="APPLICATION">%1$s</xliff:g> ukuthi ufinyelele ezintweni eziphuma ne-USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vumela insiza <xliff:g id="APPLICATION">%1$s</xliff:g> ukuthi ufinyelele ezintweni eziphuma ne-USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vula <xliff:g id="ACTIVITY">%1$s</xliff:g> uma ledivayisi ye-USB ixhunyiwe?"</string>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0a63840..897b8d0 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -909,7 +909,7 @@
mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
}
- mVibrator = new Vibrator();
+ mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 2ba821e..c0eb1b9 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -161,12 +161,14 @@
const InputDeviceIdentifier& identifier) :
next(NULL),
fd(fd), id(id), path(path), identifier(identifier),
- classes(0), configuration(NULL), virtualKeyMap(NULL) {
+ classes(0), configuration(NULL), virtualKeyMap(NULL),
+ ffEffectPlaying(false), ffEffectId(-1) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
memset(swBitmask, 0, sizeof(swBitmask));
memset(ledBitmask, 0, sizeof(ledBitmask));
+ memset(ffBitmask, 0, sizeof(ffBitmask));
memset(propBitmask, 0, sizeof(propBitmask));
}
@@ -534,6 +536,62 @@
return NULL;
}
+void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && !device->isVirtual()) {
+ ff_effect effect;
+ memset(&effect, 0, sizeof(effect));
+ effect.type = FF_RUMBLE;
+ effect.id = device->ffEffectId;
+ effect.u.rumble.strong_magnitude = 0xc000;
+ effect.u.rumble.weak_magnitude = 0xc000;
+ effect.replay.length = (duration + 999999LL) / 1000000LL;
+ effect.replay.delay = 0;
+ if (ioctl(device->fd, EVIOCSFF, &effect)) {
+ ALOGW("Could not upload force feedback effect to device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ device->ffEffectId = effect.id;
+
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_FF;
+ ev.code = device->ffEffectId;
+ ev.value = 1;
+ if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+ ALOGW("Could not start force feedback effect on device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ device->ffEffectPlaying = true;
+ }
+}
+
+void EventHub::cancelVibrate(int32_t deviceId) {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && !device->isVirtual()) {
+ if (device->ffEffectPlaying) {
+ device->ffEffectPlaying = false;
+
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_FF;
+ ev.code = device->ffEffectId;
+ ev.value = 0;
+ if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+ ALOGW("Could not stop force feedback effect on device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ }
+ }
+}
+
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
if (deviceId == BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
@@ -720,6 +778,11 @@
break;
}
}
+ } else if (eventItem.events & EPOLLHUP) {
+ ALOGI("Removing device %s due to epoll hang-up event.",
+ device->identifier.name.string());
+ deviceChanged = true;
+ closeDeviceLocked(device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
eventItem.events, device->identifier.name.string());
@@ -944,6 +1007,7 @@
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
+ ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
@@ -1005,6 +1069,11 @@
}
}
+ // Check whether this device supports the vibrator.
+ if (test_bit(FF_RUMBLE, device->ffBitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+ }
+
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 88159e7..51d2bac 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -113,6 +113,9 @@
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+ /* The input device has a vibrator (supports FF_RUMBLE). */
+ INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
@@ -219,6 +222,10 @@
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+ /* Control the vibrator. */
+ virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+ virtual void cancelVibrate(int32_t deviceId) = 0;
+
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
@@ -277,6 +284,9 @@
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
+ virtual void vibrate(int32_t deviceId, nsecs_t duration);
+ virtual void cancelVibrate(int32_t deviceId);
+
virtual void requestReopenDevices();
virtual void wake();
@@ -303,6 +313,7 @@
uint8_t relBitmask[(REL_MAX + 1) / 8];
uint8_t swBitmask[(SW_MAX + 1) / 8];
uint8_t ledBitmask[(LED_MAX + 1) / 8];
+ uint8_t ffBitmask[(FF_MAX + 1) / 8];
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
String8 configurationFile;
@@ -310,6 +321,9 @@
VirtualKeyMap* virtualKeyMap;
KeyMap keyMap;
+ bool ffEffectPlaying;
+ int16_t ffEffectId; // initially -1
+
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index ea614ad..8c37fbb 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -36,6 +36,9 @@
// Log debug messages about gesture detection.
#define DEBUG_GESTURES 0
+// Log debug messages about the vibrator.
+#define DEBUG_VIBRATOR 0
+
#include "InputReader.h"
#include <cutils/log.h>
@@ -237,7 +240,8 @@
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
- mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
+ mGlobalMetaState(0), mGeneration(1),
+ mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
@@ -257,18 +261,22 @@
}
void InputReader::loopOnce() {
+ int32_t oldGeneration;
int32_t timeoutMillis;
+ bool inputDevicesChanged = false;
+ Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
+ oldGeneration = mGeneration;
+ timeoutMillis = -1;
+
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
+ timeoutMillis = 0;
refreshConfigurationLocked(changes);
- }
-
- timeoutMillis = -1;
- if (mNextTimeout != LLONG_MAX) {
+ } else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
@@ -283,7 +291,8 @@
if (count) {
processEventsLocked(mEventBuffer, count);
}
- if (!count || timeoutMillis == 0) {
+
+ if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
@@ -293,8 +302,18 @@
timeoutExpiredLocked(now);
}
}
+
+ if (oldGeneration != mGeneration) {
+ inputDevicesChanged = true;
+ getInputDevicesLocked(inputDevices);
+ }
} // release lock
+ // Send out a message that the describes the changed input devices.
+ if (inputDevicesChanged) {
+ mPolicy->notifyInputDevicesChanged(inputDevices);
+ }
+
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -344,6 +363,12 @@
}
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+ return;
+ }
+
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
@@ -359,27 +384,22 @@
identifier.name.string(), device->getSources());
}
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- mDevices.add(deviceId, device);
- } else {
- ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
- delete device;
- return;
- }
+ mDevices.add(deviceId, device);
+ bumpGenerationLocked();
}
void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
InputDevice* device = NULL;
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- device = mDevices.valueAt(deviceIndex);
- mDevices.removeItemsAt(deviceIndex, 1);
- } else {
+ if (deviceIndex < 0) {
ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
return;
}
+ device = mDevices.valueAt(deviceIndex);
+ mDevices.removeItemsAt(deviceIndex, 1);
+ bumpGenerationLocked();
+
if (device->isIgnored()) {
ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
device->getId(), device->getName().string());
@@ -394,7 +414,8 @@
InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
const InputDeviceIdentifier& identifier, uint32_t classes) {
- InputDevice* device = new InputDevice(&mContext, deviceId, identifier, classes);
+ InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
+ identifier, classes);
// External devices.
if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
@@ -406,6 +427,11 @@
device->addMapper(new SwitchInputMapper(device));
}
+ // Vibrator-like devices.
+ if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+ device->addMapper(new VibratorInputMapper(device));
+ }
+
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
@@ -574,42 +600,34 @@
void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
if (when < mNextTimeout) {
mNextTimeout = when;
+ mEventHub->wake();
}
}
+int32_t InputReader::bumpGenerationLocked() {
+ return ++mGeneration;
+}
+
void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
AutoMutex _l(mLock);
*outConfiguration = mInputConfiguration;
}
-status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+void InputReader::getInputDevices(Vector<InputDeviceInfo>& outInputDevices) {
AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- return NAME_NOT_FOUND;
- }
-
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (device->isIgnored()) {
- return NAME_NOT_FOUND;
- }
-
- device->getDeviceInfo(outDeviceInfo);
- return OK;
+ getInputDevicesLocked(outInputDevices);
}
-void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
- AutoMutex _l(mLock);
-
- outDeviceIds.clear();
+void InputReader::getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices) {
+ outInputDevices.clear();
size_t numDevices = mDevices.size();
for (size_t i = 0; i < numDevices; i++) {
InputDevice* device = mDevices.valueAt(i);
if (!device->isIgnored()) {
- outDeviceIds.add(device->getId());
+ outInputDevices.push();
+ device->getDeviceInfo(&outInputDevices.editTop());
}
}
}
@@ -710,6 +728,27 @@
}
}
+void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->cancelVibrate(token);
+ }
+}
+
void InputReader::dump(String8& dump) {
AutoMutex _l(mLock);
@@ -824,6 +863,11 @@
mReader->requestTimeoutAtTimeLocked(when);
}
+int32_t InputReader::ContextImpl::bumpGeneration() {
+ // lock is already held by the input loop
+ return mReader->bumpGenerationLocked();
+}
+
InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
return mReader->mPolicy.get();
}
@@ -854,9 +898,10 @@
// --- InputDevice ---
-InputDevice::InputDevice(InputReaderContext* context, int32_t id,
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
const InputDeviceIdentifier& identifier, uint32_t classes) :
- mContext(context), mId(id), mIdentifier(identifier), mClasses(classes),
+ mContext(context), mId(id), mGeneration(generation),
+ mIdentifier(identifier), mClasses(classes),
mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
}
@@ -874,6 +919,7 @@
dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getName().string());
+ dump.appendFormat(INDENT2 "Generation: %d\n", mGeneration);
dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -983,7 +1029,7 @@
}
void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
- outDeviceInfo->initialize(mId, mIdentifier.name, mIdentifier.descriptor);
+ outDeviceInfo->initialize(mId, mGeneration, mIdentifier.name, mIdentifier.descriptor);
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
@@ -1036,6 +1082,23 @@
return result;
}
+void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputDevice::cancelVibrate(int32_t token) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->cancelVibrate(token);
+ }
+}
+
int32_t InputDevice::getMetaState() {
int32_t result = 0;
size_t numMappers = mMappers.size();
@@ -1054,6 +1117,10 @@
}
}
+void InputDevice::bumpGeneration() {
+ mGeneration = mContext->bumpGeneration();
+}
+
void InputDevice::notifyReset(nsecs_t when) {
NotifyDeviceResetArgs args(when, mId);
mContext->getListener()->notifyDeviceReset(&args);
@@ -1717,6 +1784,13 @@
return false;
}
+void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+}
+
+void InputMapper::cancelVibrate(int32_t token) {
+}
+
int32_t InputMapper::getMetaState() {
return 0;
}
@@ -1728,6 +1802,10 @@
return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
}
+void InputMapper::bumpGeneration() {
+ mDevice->bumpGeneration();
+}
+
void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
const RawAbsoluteAxisInfo& axis, const char* name) {
if (axis.valid) {
@@ -1770,6 +1848,120 @@
}
+// --- VibratorInputMapper ---
+
+VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
+ InputMapper(device), mVibrating(false) {
+}
+
+VibratorInputMapper::~VibratorInputMapper() {
+}
+
+uint32_t VibratorInputMapper::getSources() {
+ return 0;
+}
+
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setVibrator(true);
+}
+
+void VibratorInputMapper::process(const RawEvent* rawEvent) {
+ // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+}
+
+void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+#if DEBUG_VIBRATOR
+ String8 patternStr;
+ for (size_t i = 0; i < patternSize; i++) {
+ if (i != 0) {
+ patternStr.append(", ");
+ }
+ patternStr.appendFormat("%lld", pattern[i]);
+ }
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
+ getDeviceId(), patternStr.string(), repeat, token);
+#endif
+
+ mVibrating = true;
+ memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
+ mPatternSize = patternSize;
+ mRepeat = repeat;
+ mToken = token;
+ mIndex = -1;
+
+ nextStep();
+}
+
+void VibratorInputMapper::cancelVibrate(int32_t token) {
+#if DEBUG_VIBRATOR
+ ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+#endif
+
+ if (mVibrating && mToken == token) {
+ stopVibrating();
+ }
+}
+
+void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+ if (mVibrating) {
+ if (when >= mNextStepTime) {
+ nextStep();
+ } else {
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+ }
+ }
+}
+
+void VibratorInputMapper::nextStep() {
+ mIndex += 1;
+ if (size_t(mIndex) >= mPatternSize) {
+ if (mRepeat < 0) {
+ // We are done.
+ stopVibrating();
+ return;
+ }
+ mIndex = mRepeat;
+ }
+
+ bool vibratorOn = mIndex & 1;
+ nsecs_t duration = mPattern[mIndex];
+ if (vibratorOn) {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
+ getDeviceId(), duration);
+#endif
+ getEventHub()->vibrate(getDeviceId(), duration);
+ } else {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+ }
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mNextStepTime = now + duration;
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+#endif
+}
+
+void VibratorInputMapper::stopVibrating() {
+ mVibrating = false;
+#if DEBUG_VIBRATOR
+ ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+}
+
+void VibratorInputMapper::dump(String8& dump) {
+ dump.append(INDENT2 "Vibrator Input Mapper:\n");
+ dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+}
+
+
// --- KeyboardInputMapper ---
KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
@@ -2137,6 +2329,7 @@
} else {
mOrientation = DISPLAY_ORIENTATION_0;
}
+ bumpGeneration();
}
}
@@ -2998,6 +3191,7 @@
// Inform the dispatcher about the changes.
*outResetNeeded = true;
+ bumpGeneration();
}
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 8ab5905..ed57596 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -33,6 +33,14 @@
#include <stddef.h>
#include <unistd.h>
+// Maximum supported size of a vibration pattern.
+// Must be at least 2.
+#define MAX_VIBRATE_PATTERN_SIZE 100
+
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+
namespace android {
class InputDevice;
@@ -209,6 +217,11 @@
/* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+
+ /* Notifies the input reader policy that some input devices have changed
+ * and provides information about all current input devices.
+ */
+ virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
};
@@ -240,16 +253,11 @@
*/
virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
- /* Gets information about the specified input device.
- * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
- * was no such device.
+ /* Gets information about all input devices.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
-
- /* Gets the list of all registered device ids. */
- virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+ virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
/* Query current input state. */
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
@@ -267,6 +275,11 @@
* The changes flag is a bitfield that indicates what has changed and whether
* the input devices must all be reopened. */
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
+
+ /* Controls the vibrator of a particular input device. */
+ virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) = 0;
+ virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
};
@@ -288,6 +301,7 @@
virtual void fadePointer() = 0;
virtual void requestTimeoutAtTime(nsecs_t when) = 0;
+ virtual int32_t bumpGeneration() = 0;
virtual InputReaderPolicyInterface* getPolicy() = 0;
virtual InputListenerInterface* getListener() = 0;
@@ -319,9 +333,7 @@
virtual void loopOnce();
virtual void getInputConfiguration(InputConfiguration* outConfiguration);
-
- virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
- virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
+ virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices);
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
int32_t scanCode);
@@ -335,6 +347,10 @@
virtual void requestRefreshConfiguration(uint32_t changes);
+ virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token);
+ virtual void cancelVibrate(int32_t deviceId, int32_t token);
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual InputDevice* createDeviceLocked(int32_t deviceId,
@@ -353,6 +369,7 @@
InputDevice* device, int32_t keyCode, int32_t scanCode);
virtual void fadePointer();
virtual void requestTimeoutAtTime(nsecs_t when);
+ virtual int32_t bumpGeneration();
virtual InputReaderPolicyInterface* getPolicy();
virtual InputListenerInterface* getListener();
virtual EventHubInterface* getEventHub();
@@ -393,9 +410,14 @@
void fadePointerLocked();
+ int32_t mGeneration;
+ int32_t bumpGenerationLocked();
+
InputConfiguration mInputConfiguration;
void updateInputConfigurationLocked();
+ void getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices);
+
nsecs_t mDisableVirtualKeysTimeout;
void disableVirtualKeysUntilLocked(nsecs_t time);
bool shouldDropVirtualKeyLocked(nsecs_t now,
@@ -432,12 +454,13 @@
/* Represents the state of a single input device. */
class InputDevice {
public:
- InputDevice(InputReaderContext* context, int32_t id,
+ InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
const InputDeviceIdentifier& identifier, uint32_t classes);
~InputDevice();
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() { return mId; }
+ inline int32_t getGeneration() { return mGeneration; }
inline const String8& getName() { return mIdentifier.name; }
inline uint32_t getClasses() { return mClasses; }
inline uint32_t getSources() { return mSources; }
@@ -460,11 +483,15 @@
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+ void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void cancelVibrate(int32_t token);
int32_t getMetaState();
void fadePointer();
+ void bumpGeneration();
+
void notifyReset(nsecs_t when);
inline const PropertyMap& getConfiguration() { return mConfiguration; }
@@ -487,6 +514,7 @@
private:
InputReaderContext* mContext;
int32_t mId;
+ int32_t mGeneration;
InputDeviceIdentifier mIdentifier;
uint32_t mClasses;
@@ -839,6 +867,9 @@
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token);
+ virtual void cancelVibrate(int32_t token);
virtual int32_t getMetaState();
@@ -849,6 +880,7 @@
InputReaderContext* mContext;
status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+ void bumpGeneration();
static void dumpRawAbsoluteAxisInfo(String8& dump,
const RawAbsoluteAxisInfo& axis, const char* name);
@@ -870,6 +902,35 @@
};
+class VibratorInputMapper : public InputMapper {
+public:
+ VibratorInputMapper(InputDevice* device);
+ virtual ~VibratorInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token);
+ virtual void cancelVibrate(int32_t token);
+ virtual void timeoutExpired(nsecs_t when);
+ virtual void dump(String8& dump);
+
+private:
+ bool mVibrating;
+ nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
+ size_t mPatternSize;
+ ssize_t mRepeat;
+ int32_t mToken;
+ ssize_t mIndex;
+ nsecs_t mNextStepTime;
+
+ void nextStep();
+ void stopVibrating();
+};
+
+
class KeyboardInputMapper : public InputMapper {
public:
KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index eac9a1c..94d4189 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -127,6 +127,7 @@
class FakeInputReaderPolicy : public InputReaderPolicyInterface {
InputReaderConfiguration mConfig;
KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
+ Vector<InputDeviceInfo> mInputDevices;
protected:
virtual ~FakeInputReaderPolicy() { }
@@ -141,10 +142,6 @@
mConfig.setDisplayInfo(displayId, true /*external*/, width, height, orientation);
}
- virtual nsecs_t getVirtualKeyQuietTime() {
- return 0;
- }
-
void addExcludedDeviceName(const String8& deviceName) {
mConfig.excludedDeviceNames.push(deviceName);
}
@@ -157,6 +154,10 @@
return &mConfig;
}
+ const Vector<InputDeviceInfo>& getInputDevices() const {
+ return mInputDevices;
+ }
+
private:
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
@@ -165,6 +166,10 @@
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
return mPointerControllers.valueFor(deviceId);
}
+
+ virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
+ mInputDevices = inputDevices;
+ }
};
@@ -641,6 +646,12 @@
return NULL;
}
+ virtual void vibrate(int32_t deviceId, nsecs_t duration) {
+ }
+
+ virtual void cancelVibrate(int32_t deviceId) {
+ }
+
virtual bool isExternal(int32_t deviceId) const {
return false;
}
@@ -667,6 +678,7 @@
sp<InputListenerInterface> mListener;
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
public:
FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
@@ -722,6 +734,10 @@
virtual void requestTimeoutAtTime(nsecs_t when) {
}
+
+ virtual int32_t bumpGeneration() {
+ return ++mGeneration;
+ }
};
@@ -887,7 +903,8 @@
InputDevice* newDevice(int32_t deviceId, const String8& name, uint32_t classes) {
InputDeviceIdentifier identifier;
identifier.name = name;
- return new InputDevice(&mContext, deviceId, identifier, classes);
+ int32_t generation = deviceId + 1;
+ return new InputDevice(&mContext, deviceId, generation, identifier, classes);
}
protected:
@@ -1045,52 +1062,30 @@
ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
}
-TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) {
+TEST_F(InputReaderTest, GetInputDevices) {
ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
INPUT_DEVICE_CLASS_KEYBOARD, NULL));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"),
+ 0, NULL)); // no classes so device will be ignored
- InputDeviceInfo info;
- status_t result = mReader->getInputDeviceInfo(1, &info);
+ Vector<InputDeviceInfo> inputDevices;
+ mReader->getInputDevices(inputDevices);
- ASSERT_EQ(OK, result);
- ASSERT_EQ(1, info.getId());
- ASSERT_STREQ("keyboard", info.getName().string());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, info.getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, info.getSources());
- ASSERT_EQ(size_t(0), info.getMotionRanges().size());
-}
+ ASSERT_EQ(1U, inputDevices.size());
+ ASSERT_EQ(1, inputDevices[0].getId());
+ ASSERT_STREQ("keyboard", inputDevices[0].getName().string());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
+ ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsInvalid) {
- InputDeviceInfo info;
- status_t result = mReader->getInputDeviceInfo(-1, &info);
-
- ASSERT_EQ(NAME_NOT_FOUND, result);
-}
-
-TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) {
- addDevice(1, String8("ignored"), 0, NULL); // no classes so device will be ignored
-
- InputDeviceInfo info;
- status_t result = mReader->getInputDeviceInfo(1, &info);
-
- ASSERT_EQ(NAME_NOT_FOUND, result);
-}
-
-TEST_F(InputReaderTest, GetInputDeviceIds) {
- sp<FakePointerController> controller = new FakePointerController();
- mFakePolicy->setPointerController(2, controller);
-
- ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
- INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY, NULL));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("mouse"),
- INPUT_DEVICE_CLASS_CURSOR, NULL));
-
- Vector<int32_t> ids;
- mReader->getInputDeviceIds(ids);
-
- ASSERT_EQ(size_t(2), ids.size());
- ASSERT_EQ(1, ids[0]);
- ASSERT_EQ(2, ids[1]);
+ // Should also have received a notification describing the new input devices.
+ inputDevices = mFakePolicy->getInputDevices();
+ ASSERT_EQ(1U, inputDevices.size());
+ ASSERT_EQ(1, inputDevices[0].getId());
+ ASSERT_STREQ("keyboard", inputDevices[0].getName().string());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
+ ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
}
TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
@@ -1243,6 +1238,7 @@
protected:
static const char* DEVICE_NAME;
static const int32_t DEVICE_ID;
+ static const int32_t DEVICE_GENERATION;
static const uint32_t DEVICE_CLASSES;
sp<FakeEventHub> mFakeEventHub;
@@ -1261,7 +1257,8 @@
mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, identifier, DEVICE_CLASSES);
+ mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+ identifier, DEVICE_CLASSES);
}
virtual void TearDown() {
@@ -1276,6 +1273,7 @@
const char* InputDeviceTest::DEVICE_NAME = "device";
const int32_t InputDeviceTest::DEVICE_ID = 1;
+const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
| INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
@@ -1428,6 +1426,7 @@
protected:
static const char* DEVICE_NAME;
static const int32_t DEVICE_ID;
+ static const int32_t DEVICE_GENERATION;
static const uint32_t DEVICE_CLASSES;
sp<FakeEventHub> mFakeEventHub;
@@ -1443,7 +1442,8 @@
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, identifier, DEVICE_CLASSES);
+ mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+ identifier, DEVICE_CLASSES);
mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
}
@@ -1522,6 +1522,7 @@
const char* InputMapperTest::DEVICE_NAME = "device";
const int32_t InputMapperTest::DEVICE_ID = 1;
+const int32_t InputMapperTest::DEVICE_GENERATION = 2;
const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 352decf..359074a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -875,6 +875,19 @@
return null;
}
+ @Override
+ public boolean isActiveNetworkMetered() {
+ enforceAccessPermission();
+ final NetworkState state = getNetworkStateUnchecked(mActiveDefaultNetwork);
+ if (state != null) {
+ try {
+ return mPolicyManager.isNetworkMetered(state);
+ } catch (RemoteException e) {
+ }
+ }
+ return false;
+ }
+
public boolean setRadios(boolean turnOn) {
boolean result = true;
enforceChangePermission();
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3db4601..b22be76 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -100,7 +100,7 @@
private int mDisabledNotifications;
private NotificationRecord mVibrateNotification;
- private Vibrator mVibrator = new Vibrator();
+ private Vibrator mVibrator;
// for enabling and disabling notification pulse behavior
private boolean mScreenOn = true;
@@ -398,6 +398,7 @@
{
super();
mContext = context;
+ mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mAm = ActivityManagerNative.getDefault();
mSound = new NotificationPlayer(TAG);
mSound.setUsesWakeLock(context);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2d2a881..9a371c6 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -37,6 +37,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.SystemSensorManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -2946,7 +2947,7 @@
}
void systemReady() {
- mSensorManager = new SensorManager(mHandlerThread.getLooper());
+ mSensorManager = new SystemSensorManager(mHandlerThread.getLooper());
mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
// don't bother with the light sensor if auto brightness is handled in hardware
if (mUseSoftwareAutoBrightness) {
diff --git a/services/java/com/android/server/RecognitionManagerService.java b/services/java/com/android/server/RecognitionManagerService.java
index 8e55512..85224d8 100644
--- a/services/java/com/android/server/RecognitionManagerService.java
+++ b/services/java/com/android/server/RecognitionManagerService.java
@@ -75,7 +75,10 @@
try {
mContext.getPackageManager().getServiceInfo(comp, 0);
} catch (NameNotFoundException e) {
- setCurRecognizer(null);
+ comp = findAvailRecognizer(null);
+ if (comp != null) {
+ setCurRecognizer(comp);
+ }
}
} else {
comp = findAvailRecognizer(null);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 241d04ff..75dcf8c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -115,6 +115,7 @@
LightsService lights = null;
PowerManagerService power = null;
BatteryService battery = null;
+ VibratorService vibrator = null;
AlarmManagerService alarm = null;
NetworkManagementService networkManagement = null;
NetworkStatsService networkStats = null;
@@ -203,7 +204,8 @@
ServiceManager.addService("battery", battery);
Slog.i(TAG, "Vibrator Service");
- ServiceManager.addService("vibrator", new VibratorService(context));
+ vibrator = new VibratorService(context);
+ ServiceManager.addService("vibrator", vibrator);
// only initialize the power service after we have started the
// lights service, content providers and the battery service.
@@ -645,6 +647,12 @@
// It is now time to start up the app processes...
+ try {
+ vibrator.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Vibrator Service ready", e);
+ }
+
if (devicePolicy != null) {
try {
devicePolicy.systemReady();
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index de25747..6282c31 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -21,6 +21,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.IVibratorService;
import android.os.PowerManager;
@@ -29,18 +31,41 @@
import android.os.IBinder;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.Vibrator;
import android.os.WorkSource;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import android.view.InputDevice;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
-public class VibratorService extends IVibratorService.Stub {
+public class VibratorService extends IVibratorService.Stub
+ implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
private final WorkSource mTmpWorkSource = new WorkSource();
+ private final Handler mH = new Handler();
+
+ private final Context mContext;
+ private final PowerManager.WakeLock mWakeLock;
+ private InputManager mIm;
+
+ volatile VibrateThread mThread;
+
+ // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
+ // to be acquired
+ private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
+ private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
+ private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+
+ native static boolean vibratorExists();
+ native static void vibratorOn(long milliseconds);
+ native static void vibratorOff();
private class Vibration implements IBinder.DeathRecipient {
private final IBinder mToken;
@@ -112,10 +137,23 @@
context.registerReceiver(mIntentReceiver, filter);
}
- public boolean hasVibrator() {
- return vibratorExists();
+ public void systemReady() {
+ mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
+ new ContentObserver(mH) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateInputDeviceVibrators();
+ }
+ });
+ updateInputDeviceVibrators();
}
-
+
+ public boolean hasVibrator() {
+ return doVibratorExists();
+ }
+
public void vibrate(long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -131,6 +169,7 @@
// longer than milliseconds.
return;
}
+
Vibration vib = new Vibration(token, milliseconds, uid);
synchronized (mVibrations) {
removeVibrationLocked(token);
@@ -240,7 +279,7 @@
}
mThread = null;
}
- vibratorOff();
+ doVibratorOff();
mH.removeCallbacks(mVibrationRunnable);
}
@@ -257,7 +296,7 @@
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
if (vib.mTimeout != 0) {
- vibratorOn(vib.mTimeout);
+ doVibratorOn(vib.mTimeout);
mH.postDelayed(mVibrationRunnable, vib.mTimeout);
} else {
// mThread better be null here. doCancelVibrate should always be
@@ -295,6 +334,94 @@
}
}
+ private void updateInputDeviceVibrators() {
+ synchronized (mVibrations) {
+ doCancelVibrateLocked();
+
+ synchronized (mInputDeviceVibrators) {
+ mVibrateInputDevicesSetting = false;
+ try {
+ mVibrateInputDevicesSetting = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.VIBRATE_INPUT_DEVICES) > 0;
+ } catch (SettingNotFoundException snfe) {
+ }
+
+ if (mVibrateInputDevicesSetting) {
+ if (!mInputDeviceListenerRegistered) {
+ mInputDeviceListenerRegistered = true;
+ mIm.registerInputDeviceListener(this, mH);
+ }
+ } else {
+ if (mInputDeviceListenerRegistered) {
+ mInputDeviceListenerRegistered = false;
+ mIm.unregisterInputDeviceListener(this);
+ }
+ }
+
+ mInputDeviceVibrators.clear();
+ if (mVibrateInputDevicesSetting) {
+ int[] ids = mIm.getInputDeviceIds();
+ for (int i = 0; i < ids.length; i++) {
+ InputDevice device = mIm.getInputDevice(ids[i]);
+ Vibrator vibrator = device.getVibrator();
+ if (vibrator.hasVibrator()) {
+ mInputDeviceVibrators.add(vibrator);
+ }
+ }
+ }
+ }
+
+ startNextVibrationLocked();
+ }
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ updateInputDeviceVibrators();
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ updateInputDeviceVibrators();
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ updateInputDeviceVibrators();
+ }
+
+ private boolean doVibratorExists() {
+ synchronized (mInputDeviceVibrators) {
+ return !mInputDeviceVibrators.isEmpty() || vibratorExists();
+ }
+ }
+
+ private void doVibratorOn(long millis) {
+ synchronized (mInputDeviceVibrators) {
+ final int vibratorCount = mInputDeviceVibrators.size();
+ if (vibratorCount != 0) {
+ for (int i = 0; i < vibratorCount; i++) {
+ mInputDeviceVibrators.get(i).vibrate(millis);
+ }
+ } else {
+ vibratorOn(millis);
+ }
+ }
+ }
+
+ private void doVibratorOff() {
+ synchronized (mInputDeviceVibrators) {
+ final int vibratorCount = mInputDeviceVibrators.size();
+ if (vibratorCount != 0) {
+ for (int i = 0; i < vibratorCount; i++) {
+ mInputDeviceVibrators.get(i).cancel();
+ }
+ } else {
+ vibratorOff();
+ }
+ }
+ }
+
private class VibrateThread extends Thread {
final Vibration mVibration;
boolean mDone;
@@ -350,7 +477,7 @@
// duration is saved for delay() at top of loop
duration = pattern[index++];
if (duration > 0) {
- VibratorService.this.vibratorOn(duration);
+ VibratorService.this.doVibratorOn(duration);
}
} else {
if (repeat < 0) {
@@ -394,15 +521,4 @@
}
}
};
-
- private Handler mH = new Handler();
-
- private final Context mContext;
- private final PowerManager.WakeLock mWakeLock;
-
- volatile VibrateThread mThread;
-
- native static boolean vibratorExists();
- native static void vibratorOn(long milliseconds);
- native static void vibratorOff();
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 31aa21e..889fbe4 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import com.android.server.accessibility.TouchExplorer.GestureListener;
import com.android.server.input.InputFilter;
import android.content.Context;
@@ -36,6 +37,8 @@
private final Context mContext;
+ private final GestureListener mGestureListener;
+
/**
* This is an interface for explorers that take a {@link MotionEvent}
* stream and perform touch exploration of the screen content.
@@ -64,11 +67,13 @@
}
private TouchExplorer mTouchExplorer;
+
private int mTouchscreenSourceDeviceId;
- public AccessibilityInputFilter(Context context) {
+ public AccessibilityInputFilter(Context context, GestureListener gestureListener) {
super(context.getMainLooper());
mContext = context;
+ mGestureListener = gestureListener;
}
@Override
@@ -76,7 +81,7 @@
if (DEBUG) {
Slog.d(TAG, "Accessibility input filter installed.");
}
- mTouchExplorer = new TouchExplorer(this, mContext);
+ mTouchExplorer = new TouchExplorer(this, mContext, mGestureListener);
super.onInstalled();
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index c99aa02..754a4dd 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,11 +16,14 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityServiceInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.accessibilityservice.IEventListener;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +38,7 @@
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -55,8 +59,7 @@
import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.accessibility.TouchExplorer.GestureListener;
import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -80,7 +83,7 @@
* @hide
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
- implements HandlerCaller.Callback {
+ implements GestureListener {
private static final boolean DEBUG = false;
@@ -93,12 +96,8 @@
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
- private static final int DO_SET_SERVICE_INFO = 10;
-
private static int sNextWindowId;
- final HandlerCaller mCaller;
-
final Context mContext;
final Object mLock = new Object();
@@ -152,7 +151,7 @@
int eventType = message.arg1;
synchronized (mLock) {
- notifyEventListenerLocked(service, eventType);
+ notifyAccessibilityEventLocked(service, eventType);
}
}
};
@@ -165,7 +164,6 @@
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
- mCaller = new HandlerCaller(context, this);
mWindowManagerService = (WindowManagerService) ServiceManager.getService(
Context.WINDOW_SERVICE);
mSecurityPolicy = new SecurityPolicy();
@@ -395,32 +393,6 @@
}
}
- public void executeMessage(Message message) {
- switch (message.what) {
- case DO_SET_SERVICE_INFO: {
- SomeArgs arguments = ((SomeArgs) message.obj);
-
- AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
- Service service = (Service) arguments.arg2;
-
- synchronized (mLock) {
- // If the XML manifest had data to configure the service its info
- // should be already set. In such a case update only the dynamically
- // configurable properties.
- AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
- if (oldInfo != null) {
- oldInfo.updateDynamicallyConfigurableProperties(info);
- service.setDynamicallyConfigurableProperties(oldInfo);
- } else {
- service.setDynamicallyConfigurableProperties(info);
- }
- }
- } return;
- default:
- Slog.w(LOG_TAG, "Unknown message type: " + message.what);
- }
- }
-
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) throws RemoteException {
synchronized (mLock) {
@@ -455,7 +427,7 @@
}
}
- public void registerUiTestAutomationService(IEventListener listener,
+ public void registerUiTestAutomationService(IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
@@ -480,18 +452,45 @@
}
// Hook the automation service up.
mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true);
- mUiAutomationService.onServiceConnected(componentName, listener.asBinder());
+ mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder());
}
- public void unregisterUiTestAutomationService(IEventListener listener) {
+ public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
// Automation service is not bound, so pretend it died to perform clean up.
- if (mUiAutomationService != null) {
+ if (mUiAutomationService != null
+ && mUiAutomationService.mServiceInterface == serviceClient) {
mUiAutomationService.binderDied();
}
}
}
+ @Override
+ public void onGesture(int gestureId) {
+ synchronized (mLock) {
+ final boolean dispatched = notifyGestureLocked(gestureId, false);
+ if (!dispatched) {
+ notifyGestureLocked(gestureId, true);
+ }
+ }
+ }
+
+ private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
+ final int serviceCount = mServices.size();
+ for (int i = 0; i < serviceCount; i++) {
+ Service service = mServices.get(i);
+ if (service.mIsDefault == isDefault) {
+ try {
+ service.mServiceInterface.onGesture(gestureId);
+ return true;
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error dispatching gesture.");
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Removes an AccessibilityInteractionConnection.
*
@@ -588,13 +587,13 @@
}
/**
- * Notifies a service for a scheduled event given the event type.
+ * Notifies an accessibility service client for a scheduled event given the event type.
*
- * @param service The service.
+ * @param service The service client.
* @param eventType The type of the event to dispatch.
*/
- private void notifyEventListenerLocked(Service service, int eventType) {
- IEventListener listener = service.mServiceInterface;
+ private void notifyAccessibilityEventLocked(Service service, int eventType) {
+ IAccessibilityServiceClient listener = service.mServiceInterface;
// If the service died/was disabled while the message for dispatching
// the accessibility event was propagating the listener may be null.
@@ -699,6 +698,11 @@
return false;
}
+ if (!event.isImportantForAccessibility()
+ && !service.mIncludeNotImportantViews) {
+ return false;
+ }
+
int eventType = event.getEventType();
if ((service.mEventTypes & eventType) != eventType) {
return false;
@@ -864,7 +868,7 @@
if (!mHasInputFilter) {
mHasInputFilter = true;
if (mInputFilter == null) {
- mInputFilter = new AccessibilityInputFilter(mContext);
+ mInputFilter = new AccessibilityInputFilter(mContext, this);
}
mWindowManagerService.setInputFilter(mInputFilter);
}
@@ -942,7 +946,7 @@
IBinder mService;
- IEventListener mServiceInterface;
+ IAccessibilityServiceClient mServiceInterface;
int mEventTypes;
@@ -952,6 +956,8 @@
boolean mIsDefault;
+ boolean mIncludeNotImportantViews;
+
long mNotificationTimeout;
ComponentName mComponentName;
@@ -983,6 +989,7 @@
mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
} else {
mCanRetrieveScreenContent = true;
+ mIncludeNotImportantViews = true;
}
setDynamicallyConfigurableProperties(accessibilityServiceInfo);
}
@@ -995,7 +1002,17 @@
mPackageNames.addAll(Arrays.asList(packageNames));
}
mNotificationTimeout = info.notificationTimeout;
- mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+ mIsDefault = (info.flags & DEFAULT) != 0;
+
+ final int targetSdkVersion =
+ info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
+ // TODO: Uncomment this line and remove the line below when JellyBean
+ // SDK version is finalized.
+ // if (targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+ if (targetSdkVersion > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ mIncludeNotImportantViews =
+ (info.flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ }
synchronized (mLock) {
tryAddServiceLocked(this);
@@ -1043,13 +1060,33 @@
return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
}
- public void setServiceInfo(AccessibilityServiceInfo info) {
- mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
+ @Override
+ public AccessibilityServiceInfo getServiceInfo() {
+ synchronized (mLock) {
+ return mAccessibilityServiceInfo;
+ }
}
+ @Override
+ public void setServiceInfo(AccessibilityServiceInfo info) {
+ synchronized (mLock) {
+ // If the XML manifest had data to configure the service its info
+ // should be already set. In such a case update only the dynamically
+ // configurable properties.
+ AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
+ if (oldInfo != null) {
+ oldInfo.updateDynamicallyConfigurableProperties(info);
+ setDynamicallyConfigurableProperties(oldInfo);
+ } else {
+ setDynamicallyConfigurableProperties(info);
+ }
+ }
+ }
+
+ @Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mService = service;
- mServiceInterface = IEventListener.Stub.asInterface(service);
+ mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
try {
mServiceInterface.setConnection(this, mId);
synchronized (mLock) {
@@ -1060,6 +1097,7 @@
}
}
+ @Override
public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
long accessibilityNodeId, int viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1078,11 +1116,13 @@
}
}
}
+ final int flags = (mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
- interactionId, callback, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
@@ -1093,6 +1133,7 @@
return getCompatibilityScale(resolvedWindowId);
}
+ @Override
public float findAccessibilityNodeInfosByText(int accessibilityWindowId,
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1112,11 +1153,13 @@
}
}
}
+ final int flags = (mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
- interactionId, callback, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
@@ -1127,10 +1170,47 @@
return getCompatibilityScale(resolvedWindowId);
}
+ @Override
public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long interrogatingTid,
- int prefetchFlags)
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ long interrogatingTid) throws RemoteException {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ final boolean permissionGranted =
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
+ if (!permissionGranted) {
+ return 0;
+ } else {
+ connection = getConnectionLocked(resolvedWindowId);
+ if (connection == null) {
+ return 0;
+ }
+ }
+ }
+ final int allFlags = flags | ((mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0);
+ final int interrogatingPid = Binder.getCallingPid();
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
+ interactionId, callback, allFlags, interrogatingPid, interrogatingTid);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return getCompatibilityScale(resolvedWindowId);
+ }
+
+ @Override
+ public float findFocus(int accessibilityWindowId, long accessibilityNodeId,
+ int focusType, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
IAccessibilityInteractionConnection connection = null;
@@ -1147,14 +1227,16 @@
}
}
}
+ final int flags = (mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
- interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
+ connection.findFocus(accessibilityNodeId, interactionId, focusType, callback,
+ flags, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
+ Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
@@ -1162,6 +1244,44 @@
return getCompatibilityScale(resolvedWindowId);
}
+ @Override
+ public float focusSearch(int accessibilityWindowId, long accessibilityNodeId,
+ int direction, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+ throws RemoteException {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ final boolean permissionGranted =
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
+ if (!permissionGranted) {
+ return 0;
+ } else {
+ connection = getConnectionLocked(resolvedWindowId);
+ if (connection == null) {
+ return 0;
+ }
+ }
+ }
+ final int flags = (mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+ final int interrogatingPid = Binder.getCallingPid();
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ connection.focusSearch(accessibilityNodeId, interactionId, direction, callback,
+ flags, interrogatingPid, interrogatingTid);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return getCompatibilityScale(resolvedWindowId);
+ }
+
+ @Override
public boolean performAccessibilityAction(int accessibilityWindowId,
long accessibilityNodeId, int action, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
@@ -1179,11 +1299,13 @@
}
}
}
+ final int flags = (mIncludeNotImportantViews) ?
+ AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
connection.performAccessibilityAction(accessibilityNodeId, action, interactionId,
- callback, interrogatingPid, interrogatingTid);
+ callback, flags, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
@@ -1263,22 +1385,35 @@
}
final class SecurityPolicy {
- private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
- | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
- | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+ private static final int VALID_ACTIONS =
+ AccessibilityNodeInfo.ACTION_CLICK
+ | AccessibilityNodeInfo.ACTION_FOCUS
+ | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS
+ | AccessibilityNodeInfo.ACTION_SELECT
+ | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
+ | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
+ | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
- AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
- | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
- | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
- | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
+ AccessibilityEvent.TYPE_VIEW_CLICKED
+ | AccessibilityEvent.TYPE_VIEW_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+ | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+ | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+ | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_SELECTED
| AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
| AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
- | AccessibilityEvent.TYPE_VIEW_SCROLLED;
+ | AccessibilityEvent.TYPE_VIEW_SCROLLED
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
- | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+ | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
private int mRetrievalAlowingWindowId;
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index d07aa7a..5d01c77 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -20,17 +20,25 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
import android.content.Context;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.gesture.GestureLibrary;
+import android.gesture.GesturePoint;
+import android.gesture.GestureStroke;
+import android.gesture.Prediction;
import android.os.Handler;
import android.util.Slog;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import com.android.server.accessibility.AccessibilityInputFilter.Explorer;
import com.android.server.input.InputFilter;
+import com.android.internal.R;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -54,34 +62,24 @@
*
* @hide
*/
-public class TouchExplorer implements Explorer {
+public class TouchExplorer {
+
private static final boolean DEBUG = false;
// Tag for logging received events.
- private static final String LOG_TAG_RECEIVED = "TouchExplorer-RECEIVED";
- // Tag for logging injected events.
- private static final String LOG_TAG_INJECTED = "TouchExplorer-INJECTED";
- // Tag for logging the current state.
- private static final String LOG_TAG_STATE = "TouchExplorer-STATE";
+ private static final String LOG_TAG = "TouchExplorer";
// States this explorer can be in.
private static final int STATE_TOUCH_EXPLORING = 0x00000001;
private static final int STATE_DRAGGING = 0x00000002;
private static final int STATE_DELEGATING = 0x00000004;
-
- // Invalid pointer ID.
- private static final int INVALID_POINTER_ID = -1;
+ private static final int STATE_GESTURE_DETECTING = 0x00000005;
// The time slop in milliseconds for activating an item after it has
// been touch explored. Tapping on an item within this slop will perform
// a click and tapping and holding down a long press.
private static final long ACTIVATION_TIME_SLOP = 2000;
- // This constant captures the current implementation detail that
- // pointer IDs are between 0 and 31 inclusive (subject to change).
- // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
- private static final int MAX_POINTER_COUNT = 32;
-
// The minimum of the cosine between the vectors of two moving
// pointers so they can be considered moving in the same direction.
private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
@@ -92,6 +90,14 @@
// Constant referring to the ids bits of all pointers.
private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
+ // This constant captures the current implementation detail that
+ // pointer IDs are between 0 and 31 inclusive (subject to change).
+ // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ public static final int MAX_POINTER_COUNT = 32;
+
+ // Invalid pointer ID.
+ public static final int INVALID_POINTER_ID = -1;
+
// Temporary array for storing pointer IDs.
private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
@@ -103,10 +109,6 @@
// which delegates event processing to this touch explorer.
private final InputFilter mInputFilter;
- // Helper class for tracking pointers on the screen, for example which
- // pointers are down, which are active, etc.
- private final PointerTracker mPointerTracker;
-
// Handle to the accessibility manager for firing accessibility events
// announcing touch exploration gesture start and end.
private final AccessibilityManager mAccessibilityManager;
@@ -132,21 +134,48 @@
// Command for delayed sending of a long press.
private final PerformLongPressDelayed mPerformLongPressDelayed;
+ private VelocityTracker mVelocityTracker;
+
+ private final ReceivedPointerTracker mReceivedPointerTracker;
+
+ private final InjectedPointerTracker mInjectedPointerTracker;
+
+ private final GestureListener mGestureListener;
+
+ /**
+ * Callback for gesture detection.
+ */
+ public interface GestureListener {
+
+ /**
+ * Called when a given gesture was performed.
+ *
+ * @param gestureId The gesture id.
+ */
+ public void onGesture(int gestureId);
+ }
+
/**
* Creates a new instance.
*
* @param inputFilter The input filter associated with this explorer.
* @param context A context handle for accessing resources.
*/
- public TouchExplorer(InputFilter inputFilter, Context context) {
+ public TouchExplorer(InputFilter inputFilter, Context context,
+ GestureListener gestureListener) {
+ mGestureListener = gestureListener;
+ mReceivedPointerTracker = new ReceivedPointerTracker(context);
+ mInjectedPointerTracker = new InjectedPointerTracker();
mInputFilter = inputFilter;
mTouchExplorationTapSlop =
- ViewConfiguration.get(context).getScaledTouchExplorationTapSlop();
- mPointerTracker = new PointerTracker(context);
+ ViewConfiguration.get(context).getScaledTouchExploreTapSlop();
mHandler = new Handler(context.getMainLooper());
mSendHoverDelayed = new SendHoverDelayed();
mPerformLongPressDelayed = new PerformLongPressDelayed();
mAccessibilityManager = AccessibilityManager.getInstance(context);
+ mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
+ mGestureLibrary.setOrientationStyle(4);
+ mGestureLibrary.load();
}
public void clear(MotionEvent event, int policyFlags) {
@@ -154,18 +183,14 @@
clear();
}
- /**
- * {@inheritDoc}
- */
public void onMotionEvent(MotionEvent event, int policyFlags) {
if (DEBUG) {
- Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+ Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
- Slog.d(LOG_TAG_STATE, getStateSymbolicName(mCurrentState));
+ Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
}
- // Keep track of the pointers's state.
- mPointerTracker.onReceivedMotionEvent(event);
+ mReceivedPointerTracker.onMotionEvent(event);
switch(mCurrentState) {
case STATE_TOUCH_EXPLORING: {
@@ -177,9 +202,11 @@
case STATE_DELEGATING: {
handleMotionEventStateDelegating(event, policyFlags);
} break;
- default: {
+ case STATE_GESTURE_DETECTING: {
+ handleMotionEventGestureDetecting(event, policyFlags);
+ } break;
+ default:
throw new IllegalStateException("Illegal state: " + mCurrentState);
- }
}
}
@@ -190,8 +217,14 @@
* @param policyFlags The policy flags associated with the event.
*/
private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
- PointerTracker pointerTracker = mPointerTracker;
- final int activePointerCount = pointerTracker.getActivePointerCount();
+ ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
+ InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
+ final int activePointerCount = receivedTracker.getActivePointerCount();
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
@@ -205,9 +238,9 @@
mSendHoverDelayed.remove();
mPerformLongPressDelayed.remove();
// Send a hover for every finger down so the user gets feedback.
- final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerId = receivedTracker.getPrimaryActivePointerId();
final int pointerIdBits = (1 << pointerId);
- final int lastAction = pointerTracker.getLastInjectedHoverAction();
+ final int lastAction = injectedTracker.getLastInjectedHoverAction();
// Deliver hover enter with a delay to have a change to detect
// whether the user actually starts a scrolling gesture.
@@ -232,7 +265,7 @@
// If the down is in the time slop => schedule a long press.
final long pointerDownTime =
- pointerTracker.getReceivedPointerDownTime(pointerId);
+ receivedTracker.getReceivedPointerDownTime(pointerId);
final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
final long deltaTimeExplore = pointerDownTime - lastExploreTime;
if (deltaTimeExplore <= ACTIVATION_TIME_SLOP) {
@@ -247,7 +280,7 @@
}
} break;
case MotionEvent.ACTION_MOVE: {
- final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerId = receivedTracker.getPrimaryActivePointerId();
final int pointerIndex = event.findPointerIndex(pointerId);
final int pointerIdBits = (1 << pointerId);
switch (activePointerCount) {
@@ -258,13 +291,27 @@
// Detect touch exploration gesture start by having one active pointer
// that moved more than a given distance.
if (!mTouchExploreGestureInProgress) {
- final float deltaX = pointerTracker.getReceivedPointerDownX(pointerId)
+ final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
- event.getX(pointerIndex);
- final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId)
+ final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
- event.getY(pointerIndex);
final double moveDelta = Math.hypot(deltaX, deltaY);
if (moveDelta > mTouchExplorationTapSlop) {
+
+ mVelocityTracker.computeCurrentVelocity(1000);
+ final float maxAbsVelocity = Math.max(
+ Math.abs(mVelocityTracker.getXVelocity(pointerId)),
+ Math.abs(mVelocityTracker.getYVelocity(pointerId)));
+ // TODO: Tune the velocity cut off and add a constant.
+ if (maxAbsVelocity > 1000) {
+ clear(event, policyFlags);
+ mCurrentState = STATE_GESTURE_DETECTING;
+ event.setAction(MotionEvent.ACTION_DOWN);
+ handleMotionEventGestureDetecting(event, policyFlags);
+ return;
+ }
+
mTouchExploreGestureInProgress = true;
sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
// Make sure the scheduled down/move event is sent.
@@ -272,7 +319,7 @@
mPerformLongPressDelayed.remove();
// If we have transitioned to exploring state from another one
// we need to send a hover enter event here.
- final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ final int lastAction = injectedTracker.getLastInjectedHoverAction();
if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER,
pointerIdBits, policyFlags);
@@ -355,12 +402,12 @@
} break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: {
- final int pointerId = pointerTracker.getLastReceivedUpPointerId();
+ final int pointerId = receivedTracker.getLastReceivedUpPointerId();
final int pointerIdBits = (1 << pointerId);
switch (activePointerCount) {
case 0: {
// If the pointer that went up was not active we have nothing to do.
- if (!pointerTracker.wasLastReceivedUpPointerActive()) {
+ if (!receivedTracker.wasLastReceivedUpPointerActive()) {
break;
}
@@ -381,7 +428,7 @@
if (mLastTouchExploreEvent != null) {
// If the down was not in the time slop => nothing else to do.
final long eventTime =
- pointerTracker.getLastReceivedUpPointerDownTime();
+ receivedTracker.getLastReceivedUpPointerDownTime();
final long exploreTime = mLastTouchExploreEvent.getEventTime();
final long deltaTime = eventTime - exploreTime;
if (deltaTime > ACTIVATION_TIME_SLOP) {
@@ -422,14 +469,22 @@
}
} break;
}
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ mVelocityTracker = null;
+ }
} break;
case MotionEvent.ACTION_CANCEL: {
mSendHoverDelayed.remove();
mPerformLongPressDelayed.remove();
- final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerId = receivedTracker.getPrimaryActivePointerId();
final int pointerIdBits = (1 << pointerId);
ensureHoverExitSent(event, pointerIdBits, policyFlags);
clear();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ mVelocityTracker = null;
+ }
} break;
}
}
@@ -455,7 +510,7 @@
sendDownForAllActiveNotInjectedPointers(event, policyFlags);
} break;
case MotionEvent.ACTION_MOVE: {
- final int activePointerCount = mPointerTracker.getActivePointerCount();
+ final int activePointerCount = mReceivedPointerTracker.getActivePointerCount();
switch (activePointerCount) {
case 1: {
// do nothing
@@ -487,7 +542,7 @@
}
} break;
case MotionEvent.ACTION_POINTER_UP: {
- final int activePointerCount = mPointerTracker.getActivePointerCount();
+ final int activePointerCount = mReceivedPointerTracker.getActivePointerCount();
switch (activePointerCount) {
case 1: {
// Send an event to the end of the drag gesture.
@@ -525,7 +580,8 @@
case MotionEvent.ACTION_MOVE: {
// Check whether some other pointer became active because they have moved
// a given distance and if such exist send them to the view hierarchy
- final int notInjectedCount = mPointerTracker.getNotInjectedActivePointerCount();
+ final int notInjectedCount = getNotInjectedActivePointerCount(
+ mReceivedPointerTracker, mInjectedPointerTracker);
if (notInjectedCount > 0) {
MotionEvent prototype = MotionEvent.obtain(event);
sendDownForAllActiveNotInjectedPointers(prototype, policyFlags);
@@ -533,7 +589,7 @@
} break;
case MotionEvent.ACTION_POINTER_UP: {
// No active pointers => go to initial state.
- if (mPointerTracker.getActivePointerCount() == 0) {
+ if (mReceivedPointerTracker.getActivePointerCount() == 0) {
mCurrentState = STATE_TOUCH_EXPLORING;
}
} break;
@@ -545,6 +601,72 @@
sendMotionEventStripInactivePointers(event, policyFlags);
}
+ private float mPreviousX;
+ private float mPreviousY;
+
+ private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+ private static final int TOUCH_TOLERANCE = 3;
+ private static final float MIN_PREDICTION_SCORE = 2.0f;
+
+ private GestureLibrary mGestureLibrary;
+
+ private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ final float x = event.getX();
+ final float y = event.getY();
+ mPreviousX = x;
+ mPreviousY = y;
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ final float x = event.getX();
+ final float y = event.getY();
+ final float dX = Math.abs(x - mPreviousX);
+ final float dY = Math.abs(y - mPreviousY);
+ if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
+ mPreviousX = x;
+ mPreviousY = y;
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+ }
+ } break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ float x = event.getX();
+ float y = event.getY();
+ mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+
+ Gesture gesture = new Gesture();
+ gesture.addStroke(new GestureStroke(mStrokeBuffer));
+
+ ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
+ if (!predictions.isEmpty()) {
+ Prediction bestPrediction = predictions.get(0);
+ if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
+ + bestPrediction.score);
+ }
+ try {
+ final int gestureId = Integer.parseInt(bestPrediction.name);
+ mGestureListener.onGesture(gestureId);
+ } catch (NumberFormatException nfe) {
+ Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
+ }
+ }
+ }
+
+ mStrokeBuffer.clear();
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ mStrokeBuffer.clear();
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ } break;
+ }
+ }
+
/**
* Sends down events to the view hierarchy for all active pointers which are
* not already being delivered i.e. pointers that are not yet injected.
@@ -553,14 +675,15 @@
* @param policyFlags The policy flags associated with the event.
*/
private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) {
- final PointerTracker pointerTracker = mPointerTracker;
+ ReceivedPointerTracker receivedPointers = mReceivedPointerTracker;
+ InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
int pointerIdBits = 0;
final int pointerCount = prototype.getPointerCount();
// Find which pointers are already injected.
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
- if (pointerTracker.isInjectedPointerDown(pointerId)) {
+ if (injectedPointers.isInjectedPointerDown(pointerId)) {
pointerIdBits |= (1 << pointerId);
}
}
@@ -569,11 +692,11 @@
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Skip inactive pointers.
- if (!pointerTracker.isActivePointer(pointerId)) {
+ if (!receivedPointers.isActivePointer(pointerId)) {
continue;
}
// Do not send event for already delivered pointers.
- if (pointerTracker.isInjectedPointerDown(pointerId)) {
+ if (injectedPointers.isInjectedPointerDown(pointerId)) {
continue;
}
pointerIdBits |= (1 << pointerId);
@@ -590,7 +713,7 @@
* @param policyFlags The policy flags associated with the event.
*/
private void ensureHoverExitSent(MotionEvent prototype, int pointerIdBits, int policyFlags) {
- final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ final int lastAction = mInjectedPointerTracker.getLastInjectedHoverAction();
if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
sendMotionEvent(prototype, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
policyFlags);
@@ -605,13 +728,13 @@
* @param policyFlags The policy flags associated with the event.
*/
private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
- final PointerTracker pointerTracker = mPointerTracker;
+ final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
int pointerIdBits = 0;
final int pointerCount = prototype.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Skip non injected down pointers.
- if (!pointerTracker.isInjectedPointerDown(pointerId)) {
+ if (!injectedTracked.isInjectedPointerDown(pointerId)) {
continue;
}
pointerIdBits |= (1 << pointerId);
@@ -627,18 +750,18 @@
* @param policyFlags The policy flags associated with the event.
*/
private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) {
- PointerTracker pointerTracker = mPointerTracker;
+ ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
// All pointers active therefore we just inject the event as is.
- if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) {
+ if (prototype.getPointerCount() == receivedTracker.getActivePointerCount()) {
sendMotionEvent(prototype, prototype.getAction(), ALL_POINTER_ID_BITS, policyFlags);
return;
}
// No active pointers and the one that just went up was not
// active, therefore we have nothing to do.
- if (pointerTracker.getActivePointerCount() == 0
- && !pointerTracker.wasLastReceivedUpPointerActive()) {
+ if (receivedTracker.getActivePointerCount() == 0
+ && !receivedTracker.wasLastReceivedUpPointerActive()) {
return;
}
@@ -647,7 +770,7 @@
final int actionMasked = prototype.getActionMasked();
final int actionPointerId = prototype.getPointerId(prototype.getActionIndex());
if (actionMasked != MotionEvent.ACTION_MOVE) {
- if (!pointerTracker.isActiveOrWasLastActiveUpPointer(actionPointerId)) {
+ if (!receivedTracker.isActiveOrWasLastActiveUpPointer(actionPointerId)) {
return;
}
}
@@ -658,7 +781,7 @@
final int pointerCount = prototype.getPointerCount();
for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) {
final int pointerId = prototype.getPointerId(pointerIndex);
- if (pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
+ if (receivedTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
pointerIdBits |= (1 << pointerId);
}
}
@@ -700,19 +823,20 @@
if (action == MotionEvent.ACTION_DOWN) {
event.setDownTime(event.getEventTime());
} else {
- event.setDownTime(mPointerTracker.getLastInjectedDownEventTime());
+ event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
}
if (DEBUG) {
- Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x"
+ Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
// Make sure that the user will see the event.
policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- mPointerTracker.onInjectedMotionEvent(event);
mInputFilter.sendInputEvent(event, policyFlags);
+ mInjectedPointerTracker.onMotionEvent(event);
+
if (event != prototype) {
event.recycle();
}
@@ -730,9 +854,9 @@
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
- PointerTracker pointerTracker = mPointerTracker;
+ InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
// Compute the action based on how many down pointers are injected.
- if (pointerTracker.getInjectedPointerDownCount() == 0) {
+ if (injectedTracker.getInjectedPointerDownCount() == 0) {
return MotionEvent.ACTION_DOWN;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -740,9 +864,9 @@
}
}
case MotionEvent.ACTION_POINTER_UP: {
- PointerTracker pointerTracker = mPointerTracker;
+ InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
// Compute the action based on how many down pointers are injected.
- if (pointerTracker.getInjectedPointerDownCount() == 1) {
+ if (injectedTracker.getInjectedPointerDownCount() == 1) {
return MotionEvent.ACTION_UP;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -761,9 +885,9 @@
* @return True if the gesture is a dragging one.
*/
private boolean isDraggingGesture(MotionEvent event) {
- PointerTracker pointerTracker = mPointerTracker;
+ ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
int[] pointerIds = mTempPointerIds;
- pointerTracker.populateActivePointerIds(pointerIds);
+ receivedTracker.populateActivePointerIds(pointerIds);
final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
@@ -775,9 +899,9 @@
// Check if the pointers are moving in the same direction.
final float firstDeltaX =
- firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex);
+ firstPtrX - receivedTracker.getReceivedPointerDownX(firstPtrIndex);
final float firstDeltaY =
- firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex);
+ firstPtrY - receivedTracker.getReceivedPointerDownY(firstPtrIndex);
if (firstDeltaX == 0 && firstDeltaY == 0) {
return true;
@@ -791,9 +915,9 @@
(firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
final float secondDeltaX =
- secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex);
+ secondPtrX - receivedTracker.getReceivedPointerDownX(secondPtrIndex);
final float secondDeltaY =
- secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex);
+ secondPtrY - receivedTracker.getReceivedPointerDownY(secondPtrIndex);
if (secondDeltaX == 0 && secondDeltaY == 0) {
return true;
@@ -832,7 +956,8 @@
public void clear() {
mSendHoverDelayed.remove();
mPerformLongPressDelayed.remove();
- mPointerTracker.clear();
+ mReceivedPointerTracker.clear();
+ mInjectedPointerTracker.clear();
mLastTouchExploreEvent = null;
mCurrentState = STATE_TOUCH_EXPLORING;
mTouchExploreGestureInProgress = false;
@@ -853,27 +978,253 @@
return "STATE_DRAGGING";
case STATE_DELEGATING:
return "STATE_DELEGATING";
+ case STATE_GESTURE_DETECTING:
+ return "STATE_GESTURE_DETECTING";
default:
throw new IllegalArgumentException("Unknown state: " + state);
}
}
/**
- * Helper class for tracking pointers and more specifically which of
- * them are currently down, which are active, and which are delivered
- * to the view hierarchy. The enclosing {@link TouchExplorer} uses the
- * pointer state reported by this class to perform touch exploration.
- * <p>
- * The main purpose of this class is to allow the touch explorer to
- * disregard pointers put down by accident by the user and not being
- * involved in the interaction. For example, a blind user grabs the
- * device with her left hand such that she touches the screen and she
- * uses her right hand's index finger to explore the screen content.
- * In this scenario the touches generated by the left hand are to be
- * ignored.
+ * @return The number of non injected active pointers.
*/
- class PointerTracker {
- private static final String LOG_TAG = "PointerTracker";
+ private int getNotInjectedActivePointerCount(ReceivedPointerTracker receivedTracker,
+ InjectedPointerTracker injectedTracker) {
+ final int pointerState = receivedTracker.getActivePointers()
+ & ~injectedTracker.getInjectedPointersDown();
+ return Integer.bitCount(pointerState);
+ }
+
+ /**
+ * Class for delayed sending of long press.
+ */
+ private final class PerformLongPressDelayed implements Runnable {
+ private MotionEvent mEvent;
+ private int mPolicyFlags;
+
+ public void post(MotionEvent prototype, int policyFlags, long delay) {
+ mEvent = MotionEvent.obtain(prototype);
+ mPolicyFlags = policyFlags;
+ mHandler.postDelayed(this, delay);
+ }
+
+ public void remove() {
+ if (isPenidng()) {
+ mHandler.removeCallbacks(this);
+ clear();
+ }
+ }
+
+ private boolean isPenidng() {
+ return (mEvent != null);
+ }
+
+ @Override
+ public void run() {
+ mCurrentState = STATE_DELEGATING;
+ // Make sure the scheduled hover exit is delivered.
+ mSendHoverDelayed.remove();
+ final int pointerId = mReceivedPointerTracker.getPrimaryActivePointerId();
+ final int pointerIdBits = (1 << pointerId);
+ ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
+
+ sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
+ mTouchExploreGestureInProgress = false;
+ mLastTouchExploreEvent = null;
+ clear();
+ }
+
+ private void clear() {
+ if (!isPenidng()) {
+ return;
+ }
+ mEvent.recycle();
+ mEvent = null;
+ mPolicyFlags = 0;
+ }
+ }
+
+ /**
+ * Class for delayed sending of hover events.
+ */
+ private final class SendHoverDelayed implements Runnable {
+ private MotionEvent mEvent;
+ private int mAction;
+ private int mPointerIdBits;
+ private int mPolicyFlags;
+
+ public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
+ long delay) {
+ remove();
+ mEvent = MotionEvent.obtain(prototype);
+ mAction = action;
+ mPointerIdBits = pointerIdBits;
+ mPolicyFlags = policyFlags;
+ mHandler.postDelayed(this, delay);
+ }
+
+ public void remove() {
+ mHandler.removeCallbacks(this);
+ clear();
+ }
+
+ private boolean isPenidng() {
+ return (mEvent != null);
+ }
+
+ private void clear() {
+ if (!isPenidng()) {
+ return;
+ }
+ mEvent.recycle();
+ mEvent = null;
+ mAction = 0;
+ mPointerIdBits = -1;
+ mPolicyFlags = 0;
+ }
+
+ public void forceSendAndRemove() {
+ if (isPenidng()) {
+ run();
+ remove();
+ }
+ }
+
+ public void run() {
+ if (DEBUG) {
+ if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
+ Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
+ } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
+ } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
+ }
+ }
+
+ sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
+ clear();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return LOG_TAG;
+ }
+
+ class InjectedPointerTracker {
+ private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
+
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
+
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+
+ // The action of the last injected hover event.
+ private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ public void onMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getDownTime();
+ } break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown &= ~pointerFlag;
+ if (mInjectedPointersDown == 0) {
+ mLastInjectedDownEventTime = 0;
+ }
+ } break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ mLastInjectedHoverEventAction = event.getActionMasked();
+ } break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer: " + toString());
+ }
+ }
+
+ /**
+ * Clears the internals state.
+ */
+ public void clear() {
+ mInjectedPointersDown = 0;
+ }
+
+ /**
+ * @return The time of the last injected down event.
+ */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ /**
+ * @return The number of down pointers injected to the view hierarchy.
+ */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /**
+ * @return The bits of the injected pointers that are down.
+ */
+ public int getInjectedPointersDown() {
+ return mInjectedPointersDown;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /**
+ * @return The action of the last injected hover event.
+ */
+ public int getLastInjectedHoverAction() {
+ return mLastInjectedHoverEventAction;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("=========================");
+ builder.append("\nDown pointers #");
+ builder.append(Integer.bitCount(mInjectedPointersDown));
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if ((mInjectedPointersDown & i) != 0) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\n=========================");
+ return builder.toString();
+ }
+ }
+
+ class ReceivedPointerTracker {
+ private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
// The coefficient by which to multiply
// ViewConfiguration.#getScaledTouchSlop()
@@ -902,26 +1253,19 @@
// Flag indicating that there is at least one active pointer moving.
private boolean mHasMovingActivePointer;
- // Keep track of which pointers sent to the system are down.
- private int mInjectedPointersDown;
-
// Keep track of the last up pointer data.
private long mLastReceivedUpPointerDownTime;
private int mLastReceivedUpPointerId;
private boolean mLastReceivedUpPointerActive;
-
- // The time of the last injected down.
- private long mLastInjectedDownEventTime;
-
- // The action of the last injected hover event.
- private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+ private float mLastReceivedUpPointerDownX;
+ private float mLastReceivedUpPointerDownY;
/**
* Creates a new instance.
*
* @param context Context for looking up resources.
*/
- public PointerTracker(Context context) {
+ public ReceivedPointerTracker(Context context) {
mThresholdActivePointer =
ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER;
}
@@ -937,10 +1281,11 @@
mActivePointers = 0;
mPrimaryActivePointerId = 0;
mHasMovingActivePointer = false;
- mInjectedPointersDown = 0;
mLastReceivedUpPointerDownTime = 0;
mLastReceivedUpPointerId = 0;
mLastReceivedUpPointerActive = false;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReceivedUpPointerDownY = 0;
}
/**
@@ -948,12 +1293,10 @@
*
* @param event The event to process.
*/
- public void onReceivedMotionEvent(MotionEvent event) {
+ public void onMotionEvent(MotionEvent event) {
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
- // New gesture so restart tracking injected down pointers.
- mInjectedPointersDown = 0;
handleReceivedPointerDown(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
@@ -970,39 +1313,7 @@
} break;
}
if (DEBUG) {
- Slog.i(LOG_TAG, "Received pointer: " + toString());
- }
- }
-
- /**
- * Processes an injected {@link MotionEvent} event.
- *
- * @param event The event to process.
- */
- public void onInjectedMotionEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- handleInjectedPointerDown(event.getActionIndex(), event);
- mLastInjectedDownEventTime = event.getDownTime();
- } break;
- case MotionEvent.ACTION_POINTER_DOWN: {
- handleInjectedPointerDown(event.getActionIndex(), event);
- } break;
- case MotionEvent.ACTION_UP: {
- handleInjectedPointerUp(event.getActionIndex(), event);
- } break;
- case MotionEvent.ACTION_POINTER_UP: {
- handleInjectedPointerUp(event.getActionIndex(), event);
- } break;
- case MotionEvent.ACTION_HOVER_ENTER:
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT: {
- mLastInjectedHoverEventAction = event.getActionMasked();
- } break;
- }
- if (DEBUG) {
- Slog.i(LOG_TAG, "Injected pointer: " + toString());
+ Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer: " + toString());
}
}
@@ -1014,6 +1325,13 @@
}
/**
+ * @return The bits of the pointers that are active.
+ */
+ public int getActivePointers() {
+ return mActivePointers;
+ }
+
+ /**
* @return The number of down input pointers that are active.
*/
public int getActivePointerCount() {
@@ -1032,24 +1350,6 @@
}
/**
- * Whether an injected pointer is down.
- *
- * @param pointerId The unique pointer id.
- * @return True if the pointer is down.
- */
- public boolean isInjectedPointerDown(int pointerId) {
- final int pointerFlag = (1 << pointerId);
- return (mInjectedPointersDown & pointerFlag) != 0;
- }
-
- /**
- * @return The number of down pointers injected to the view hierarchy.
- */
- public int getInjectedPointerDownCount() {
- return Integer.bitCount(mInjectedPointersDown);
- }
-
- /**
* Whether an input pointer is active.
*
* @param pointerId The unique pointer id.
@@ -1108,27 +1408,27 @@
return mLastReceivedUpPointerId;
}
+
+ /**
+ * @return The down X of the last received pointer that went up.
+ */
+ public float getLastReceivedUpPointerDownX() {
+ return mLastReceivedUpPointerDownX;
+ }
+
+ /**
+ * @return The down Y of the last received pointer that went up.
+ */
+ public float getLastReceivedUpPointerDownY() {
+ return mLastReceivedUpPointerDownY;
+ }
+
/**
* @return Whether the last received pointer that went up was active.
*/
public boolean wasLastReceivedUpPointerActive() {
return mLastReceivedUpPointerActive;
}
-
- /**
- * @return The time of the last injected down event.
- */
- public long getLastInjectedDownEventTime() {
- return mLastInjectedDownEventTime;
- }
-
- /**
- * @return The action of the last injected hover event.
- */
- public int getLastInjectedHoverAction() {
- return mLastInjectedHoverEventAction;
- }
-
/**
* Populates the active pointer IDs to the given array.
* <p>
@@ -1147,18 +1447,10 @@
}
/**
- * @return The number of non injected active pointers.
- */
- public int getNotInjectedActivePointerCount() {
- final int pointerState = mActivePointers & ~mInjectedPointersDown;
- return Integer.bitCount(pointerState);
- }
-
- /**
* @param pointerId The unique pointer id.
* @return Whether the pointer is active or was the last active than went up.
*/
- private boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
+ public boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
return (isActivePointer(pointerId)
|| (mLastReceivedUpPointerId == pointerId
&& mLastReceivedUpPointerActive));
@@ -1177,6 +1469,8 @@
mLastReceivedUpPointerId = 0;
mLastReceivedUpPointerDownTime = 0;
mLastReceivedUpPointerActive = false;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReceivedUpPointerDownX = 0;
mReceivedPointersDown |= pointerFlag;
mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
@@ -1217,6 +1511,8 @@
mLastReceivedUpPointerId = pointerId;
mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
mLastReceivedUpPointerActive = isActivePointer(pointerId);
+ mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
+ mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
mReceivedPointersDown &= ~pointerFlag;
mActivePointers &= ~pointerFlag;
@@ -1233,33 +1529,6 @@
}
/**
- * Handles a injected pointer down event.
- *
- * @param pointerIndex The index of the pointer that has changed.
- * @param event The event to be handled.
- */
- private void handleInjectedPointerDown(int pointerIndex, MotionEvent event) {
- final int pointerId = event.getPointerId(pointerIndex);
- final int pointerFlag = (1 << pointerId);
- mInjectedPointersDown |= pointerFlag;
- }
-
- /**
- * Handles a injected pointer up event.
- *
- * @param pointerIndex The index of the pointer that has changed.
- * @param event The event to be handled.
- */
- private void handleInjectedPointerUp(int pointerIndex, MotionEvent event) {
- final int pointerId = event.getPointerId(pointerIndex);
- final int pointerFlag = (1 << pointerId);
- mInjectedPointersDown &= ~pointerFlag;
- if (mInjectedPointersDown == 0) {
- mLastInjectedDownEventTime = 0;
- }
- }
-
- /**
* Detects the active pointers in an event.
*
* @param event The event to examine.
@@ -1348,117 +1617,4 @@
return builder.toString();
}
}
-
- /**
- * Class for delayed sending of long press.
- */
- private final class PerformLongPressDelayed implements Runnable {
- private MotionEvent mEvent;
- private int mPolicyFlags;
-
- public void post(MotionEvent prototype, int policyFlags, long delay) {
- mEvent = MotionEvent.obtain(prototype);
- mPolicyFlags = policyFlags;
- mHandler.postDelayed(this, delay);
- }
-
- public void remove() {
- if (isPenidng()) {
- mHandler.removeCallbacks(this);
- clear();
- }
- }
-
- private boolean isPenidng() {
- return (mEvent != null);
- }
-
- @Override
- public void run() {
- mCurrentState = STATE_DELEGATING;
- // Make sure the scheduled hover exit is delivered.
- mSendHoverDelayed.remove();
- final int pointerId = mPointerTracker.getPrimaryActivePointerId();
- final int pointerIdBits = (1 << pointerId);
- ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
-
- sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
- mTouchExploreGestureInProgress = false;
- mLastTouchExploreEvent = null;
- clear();
- }
-
- private void clear() {
- if (!isPenidng()) {
- return;
- }
- mEvent.recycle();
- mEvent = null;
- mPolicyFlags = 0;
- }
- }
-
- /**
- * Class for delayed sending of hover events.
- */
- private final class SendHoverDelayed implements Runnable {
- private static final String LOG_TAG = "SendHoverEnterOrExitDelayed";
-
- private MotionEvent mEvent;
- private int mAction;
- private int mPointerIdBits;
- private int mPolicyFlags;
-
- public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
- long delay) {
- remove();
- mEvent = MotionEvent.obtain(prototype);
- mAction = action;
- mPointerIdBits = pointerIdBits;
- mPolicyFlags = policyFlags;
- mHandler.postDelayed(this, delay);
- }
-
- public void remove() {
- mHandler.removeCallbacks(this);
- clear();
- }
-
- private boolean isPenidng() {
- return (mEvent != null);
- }
-
- private void clear() {
- if (!isPenidng()) {
- return;
- }
- mEvent.recycle();
- mEvent = null;
- mAction = 0;
- mPointerIdBits = -1;
- mPolicyFlags = 0;
- }
-
- public void forceSendAndRemove() {
- if (isPenidng()) {
- run();
- remove();
- }
- }
-
- public void run() {
- if (DEBUG) {
- if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
- Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
- } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
- Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
- } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
- Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
- }
- }
-
- sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
- clear();
- }
- }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index a098f18..cce8e7a 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -558,6 +558,11 @@
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId());
break;
+ case ActivityOptions.ANIM_SCALE_UP:
+ service.mWindowManager.overridePendingAppTransitionScaleUp(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+ break;
case ActivityOptions.ANIM_THUMBNAIL:
service.mWindowManager.overridePendingAppTransitionThumb(
pendingOptions.getThumbnail(),
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 2f25df1..e819432 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -34,18 +34,23 @@
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.hardware.input.IInputManager;
+import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -77,12 +82,39 @@
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+ private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
+
// Pointer to native input manager service object.
private final int mPtr;
private final Context mContext;
private final Callbacks mCallbacks;
- private final Handler mHandler;
+ private final InputManagerHandler mHandler;
+
+ // Used to simulate a persistent data store for keyboard layouts.
+ // TODO: Replace with the real thing.
+ private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>();
+
+ // List of currently registered input devices changed listeners by process id.
+ private Object mInputDevicesLock = new Object();
+ private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
+ private InputDevice[] mInputDevices = new InputDevice[0];
+ private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
+ new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
+ private final ArrayList<InputDevicesChangedListenerRecord>
+ mTempInputDevicesChangedListenersToNotify =
+ new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
+
+ // State for vibrator tokens.
+ private Object mVibratorLock = new Object();
+ private HashMap<IBinder, VibratorToken> mVibratorTokens =
+ new HashMap<IBinder, VibratorToken>();
+ private int mNextVibratorTokenValue;
+
+ // State for the currently installed input filter.
+ final Object mInputFilterLock = new Object();
+ InputFilter mInputFilter; // guarded by mInputFilterLock
+ InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
private static native int nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
@@ -111,13 +143,14 @@
private static native void nativeSetSystemUiVisibility(int ptr, int visibility);
private static native void nativeSetFocusedApplication(int ptr,
InputApplicationHandle application);
- private static native InputDevice nativeGetInputDevice(int ptr, int deviceId);
private static native void nativeGetInputConfiguration(int ptr, Configuration configuration);
- private static native int[] nativeGetInputDeviceIds(int ptr);
private static native boolean nativeTransferTouchFocus(int ptr,
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(int ptr, int speed);
private static native void nativeSetShowTouches(int ptr, boolean enabled);
+ private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
+ int repeat, int token);
+ private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
private static native String nativeDump(int ptr);
private static native void nativeMonitor(int ptr);
@@ -145,19 +178,10 @@
/** The key is down but is a virtual key press that is being emulated by the system. */
public static final int KEY_STATE_VIRTUAL = 2;
- // Used to simulate a persistent data store for keyboard layouts.
- // TODO: Replace with the real thing.
- private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>();
-
- // State for the currently installed input filter.
- final Object mInputFilterLock = new Object();
- InputFilter mInputFilter;
- InputFilterHost mInputFilterHost;
-
public InputManagerService(Context context, Callbacks callbacks) {
this.mContext = context;
this.mCallbacks = callbacks;
- this.mHandler = new Handler();
+ this.mHandler = new InputManagerHandler();
Slog.i(TAG, "Initializing input manager");
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
@@ -396,16 +420,98 @@
*/
@Override // Binder call
public InputDevice getInputDevice(int deviceId) {
- return nativeGetInputDevice(mPtr, deviceId);
+ synchronized (mInputDevicesLock) {
+ final int count = mInputDevices.length;
+ for (int i = 0; i < count; i++) {
+ final InputDevice inputDevice = mInputDevices[i];
+ if (inputDevice.getId() == deviceId) {
+ return inputDevice;
+ }
+ }
+ }
+ return null;
}
-
+
/**
* Gets the ids of all input devices in the system.
* @return The input device ids.
*/
@Override // Binder call
public int[] getInputDeviceIds() {
- return nativeGetInputDeviceIds(mPtr);
+ synchronized (mInputDevicesLock) {
+ final int count = mInputDevices.length;
+ int[] ids = new int[count];
+ for (int i = 0; i < count; i++) {
+ ids[i] = mInputDevices[i].getId();
+ }
+ return ids;
+ }
+ }
+
+ @Override // Binder call
+ public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mInputDevicesLock) {
+ int callingPid = Binder.getCallingPid();
+ if (mInputDevicesChangedListeners.get(callingPid) != null) {
+ throw new SecurityException("The calling process has already "
+ + "registered an InputDevicesChangedListener.");
+ }
+
+ InputDevicesChangedListenerRecord record =
+ new InputDevicesChangedListenerRecord(callingPid, listener);
+ try {
+ IBinder binder = listener.asBinder();
+ binder.linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+
+ mInputDevicesChangedListeners.put(callingPid, record);
+ }
+ }
+
+ private void onInputDevicesChangedListenerDied(int pid) {
+ synchronized (mInputDevicesLock) {
+ mInputDevicesChangedListeners.remove(pid);
+ }
+ }
+
+ // Must be called on handler.
+ private void deliverInputDevicesChanged() {
+ mTempInputDevicesChangedListenersToNotify.clear();
+
+ final int numListeners;
+ final int[] deviceIdAndGeneration;
+ synchronized (mInputDevicesLock) {
+ if (!mInputDevicesChangedPending) {
+ return;
+ }
+ mInputDevicesChangedPending = false;
+
+ numListeners = mInputDevicesChangedListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mTempInputDevicesChangedListenersToNotify.add(
+ mInputDevicesChangedListeners.valueAt(i));
+ }
+
+ final int numDevices = mInputDevices.length;
+ deviceIdAndGeneration = new int[numDevices * 2];
+ for (int i = 0; i < numDevices; i++) {
+ final InputDevice inputDevice = mInputDevices[i];
+ deviceIdAndGeneration[i * 2] = inputDevice.getId();
+ deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
+ }
+ }
+
+ for (int i = 0; i < numListeners; i++) {
+ mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
+ deviceIdAndGeneration);
+ }
}
@Override // Binder call
@@ -695,6 +801,65 @@
return result;
}
+ // Binder call
+ @Override
+ public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ VibratorToken v;
+ synchronized (mVibratorLock) {
+ v = mVibratorTokens.get(token);
+ if (v == null) {
+ v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
+ try {
+ token.linkToDeath(v, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+ mVibratorTokens.put(token, v);
+ }
+ }
+
+ synchronized (v) {
+ v.mVibrating = true;
+ nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void cancelVibrate(int deviceId, IBinder token) {
+ VibratorToken v;
+ synchronized (mVibratorLock) {
+ v = mVibratorTokens.get(token);
+ if (v == null || v.mDeviceId != deviceId) {
+ return; // nothing to cancel
+ }
+ }
+
+ cancelVibrateIfNeeded(v);
+ }
+
+ void onVibratorTokenDied(VibratorToken v) {
+ synchronized (mVibratorLock) {
+ mVibratorTokens.remove(v.mToken);
+ }
+
+ cancelVibrateIfNeeded(v);
+ }
+
+ private void cancelVibrateIfNeeded(VibratorToken v) {
+ synchronized (v) {
+ if (v.mVibrating) {
+ nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
+ v.mVibrating = false;
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -741,6 +906,18 @@
}
// Native callback.
+ private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
+ synchronized (mInputDevicesLock) {
+ mInputDevices = inputDevices;
+
+ if (!mInputDevicesChangedPending) {
+ mInputDevicesChangedPending = true;
+ mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED);
+ }
+ }
+ }
+
+ // Native callback.
private void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
@@ -906,6 +1083,20 @@
}
/**
+ * Private handler for the input manager.
+ */
+ private final class InputManagerHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DELIVER_INPUT_DEVICES_CHANGED:
+ deliverInputDevicesChanged();
+ break;
+ }
+ }
+ }
+
+ /**
* Hosting interface for input filters to call back into the input manager.
*/
private final class InputFilterHost implements InputFilter.Host {
@@ -957,4 +1148,54 @@
return result;
}
}
+
+ private final class InputDevicesChangedListenerRecord implements DeathRecipient {
+ private final int mPid;
+ private final IInputDevicesChangedListener mListener;
+
+ public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) {
+ mPid = pid;
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died.");
+ }
+ onInputDevicesChangedListenerDied(mPid);
+ }
+
+ public void notifyInputDevicesChanged(int[] info) {
+ try {
+ mListener.onInputDevicesChanged(info);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process "
+ + mPid + " that input devices changed, assuming it died.", ex);
+ binderDied();
+ }
+ }
+ }
+
+ private final class VibratorToken implements DeathRecipient {
+ public final int mDeviceId;
+ public final IBinder mToken;
+ public final int mTokenValue;
+
+ public boolean mVibrating;
+
+ public VibratorToken(int deviceId, IBinder token, int tokenValue) {
+ mDeviceId = deviceId;
+ mToken = token;
+ mTokenValue = tokenValue;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator token died.");
+ }
+ onVibratorTokenDied(this);
+ }
+ }
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index fa62e497..1e17067 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -50,6 +50,7 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute;
@@ -1216,6 +1217,23 @@
}
@Override
+ public int[] getAppsWithPolicy(int policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ int[] appIds = new int[0];
+ synchronized (mRulesLock) {
+ for (int i = 0; i < mAppPolicy.size(); i++) {
+ final int appId = mAppPolicy.keyAt(i);
+ final int appPolicy = mAppPolicy.valueAt(i);
+ if (appPolicy == policy) {
+ appIds = appendInt(appIds, appId);
+ }
+ }
+ }
+ return appIds;
+ }
+
+ @Override
public void registerListener(INetworkPolicyListener listener) {
// TODO: create permission for observing network policy
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -1373,6 +1391,22 @@
}
@Override
+ public boolean isNetworkMetered(NetworkState state) {
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+
+ final NetworkPolicy policy;
+ synchronized (mRulesLock) {
+ policy = findPolicyForNetworkLocked(ident);
+ }
+
+ if (policy != null) {
+ return policy.metered;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
@@ -1796,11 +1830,6 @@
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
- public static boolean isAirplaneModeOn(Context context) {
- return Settings.System.getInt(
- context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) != 0;
- }
-
private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 4382a03..2a67e02 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -26,6 +26,7 @@
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
@@ -33,7 +34,7 @@
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
@@ -54,6 +55,8 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.internal.util.ArrayUtils.appendElement;
+import static com.android.internal.util.ArrayUtils.contains;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
@@ -194,6 +197,8 @@
private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
/** Current default active iface. */
private String mActiveIface;
+ /** Set of any ifaces associated with mobile networks since boot. */
+ private String[] mMobileIfaces = new String[0];
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
@@ -517,6 +522,11 @@
}
@Override
+ public String[] getMobileIfaces() {
+ return mMobileIfaces;
+ }
+
+ @Override
public void incrementOperationCount(int uid, int tag, int operationCount) {
if (Binder.getCallingUid() != uid) {
mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
@@ -735,6 +745,13 @@
}
ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
+
+ // remember any ifaces associated with mobile networks
+ if (isNetworkTypeMobile(state.networkInfo.getType())) {
+ if (!contains(mMobileIfaces, iface)) {
+ mMobileIfaces = appendElement(String.class, mMobileIfaces, iface);
+ }
+ }
}
}
}
@@ -861,7 +878,7 @@
NetworkStats.Entry uidTotal;
// collect mobile sample
- template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
+ template = buildTemplateMobileWildcard();
devTotal = mDevRecorder.getTotalSinceBootLocked(template);
xtTotal = new NetworkStats.Entry();
uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
@@ -1022,12 +1039,6 @@
}
};
- private static String getActiveSubscriberId(Context context) {
- final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- return telephony.getSubscriberId();
- }
-
private boolean isBandwidthControlEnabled() {
try {
return mNetworkManager.isBandwidthControlEnabled();
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 1593707..b97d7fd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -16,14 +16,14 @@
package com.android.server.pm;
+import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.ENFORCEMENT_YES;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS;
+import static com.android.internal.util.ArrayUtils.appendInt;
+import static com.android.internal.util.ArrayUtils.removeInt;
import static libcore.io.OsConstants.S_ISLNK;
import com.android.internal.app.IMediaContainerService;
@@ -141,6 +141,7 @@
import java.util.zip.ZipOutputStream;
import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
import libcore.io.Libcore;
/**
@@ -1453,22 +1454,6 @@
}
}
- static int[] appendInt(int[] cur, int val) {
- if (cur == null) {
- return new int[] { val };
- }
- final int N = cur.length;
- for (int i=0; i<N; i++) {
- if (cur[i] == val) {
- return cur;
- }
- }
- int[] ret = new int[N+1];
- System.arraycopy(cur, 0, ret, 0, N);
- ret[N] = val;
- return ret;
- }
-
static int[] appendInts(int[] cur, int[] add) {
if (add == null) return cur;
if (cur == null) return add;
@@ -1479,26 +1464,6 @@
return cur;
}
- static int[] removeInt(int[] cur, int val) {
- if (cur == null) {
- return null;
- }
- final int N = cur.length;
- for (int i=0; i<N; i++) {
- if (cur[i] == val) {
- int[] ret = new int[N-1];
- if (i > 0) {
- System.arraycopy(cur, 0, ret, 0, i);
- }
- if (i < (N-1)) {
- System.arraycopy(cur, i + 1, ret, i, N - i - 1);
- }
- return ret;
- }
- }
- return cur;
- }
-
static int[] removeInts(int[] cur, int[] rem) {
if (rem == null) return cur;
if (cur == null) return cur;
@@ -6616,6 +6581,7 @@
oldPackage = mPackages.get(pkgName);
if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
+ Slog.w(TAG, "New package has a different signature: " + pkgName);
res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return;
}
@@ -6981,9 +6947,8 @@
} catch (IOException e) {
Slog.e(TAG, "Couldn't create a new zip file for the public parts of a" +
" forward-locked app.");
+ destResourceFile.delete();
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- } finally {
- //TODO clean up the extracted public files
}
retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath),
newPackage.applicationInfo.uid);
@@ -7025,38 +6990,34 @@
File publicZipFile) throws IOException {
final FileOutputStream fstr = new FileOutputStream(publicZipFile);
final ZipOutputStream publicZipOutStream = new ZipOutputStream(fstr);
- final ZipFile privateZip = new ZipFile(newPackage.mPath);
+ try {
+ final ZipFile privateZip = new ZipFile(newPackage.mPath);
+ try {
+ // Copy manifest, resources.arsc and res directory to public zip
- // Copy manifest, resources.arsc and res directory to public zip
-
- final Enumeration<? extends ZipEntry> privateZipEntries = privateZip.entries();
- while (privateZipEntries.hasMoreElements()) {
- final ZipEntry zipEntry = privateZipEntries.nextElement();
- final String zipEntryName = zipEntry.getName();
- if ("AndroidManifest.xml".equals(zipEntryName)
- || "resources.arsc".equals(zipEntryName)
- || zipEntryName.startsWith("res/")) {
- try {
- copyZipEntry(zipEntry, privateZip, publicZipOutStream);
- } catch (IOException e) {
- try {
- publicZipOutStream.close();
- throw e;
- } finally {
- publicZipFile.delete();
+ final Enumeration<? extends ZipEntry> privateZipEntries = privateZip.entries();
+ while (privateZipEntries.hasMoreElements()) {
+ final ZipEntry zipEntry = privateZipEntries.nextElement();
+ final String zipEntryName = zipEntry.getName();
+ if ("AndroidManifest.xml".equals(zipEntryName)
+ || "resources.arsc".equals(zipEntryName)
+ || zipEntryName.startsWith("res/")) {
+ copyZipEntry(zipEntry, privateZip, publicZipOutStream);
}
}
+ } finally {
+ try { privateZip.close(); } catch (IOException e) { }
}
- }
- publicZipOutStream.finish();
- publicZipOutStream.flush();
- FileUtils.sync(fstr);
- publicZipOutStream.close();
- FileUtils.setPermissions(
- publicZipFile.getAbsolutePath(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP|FileUtils.S_IROTH,
- -1, -1);
+ publicZipOutStream.finish();
+ publicZipOutStream.flush();
+ FileUtils.sync(fstr);
+ publicZipOutStream.close();
+ FileUtils.setPermissions(publicZipFile.getAbsolutePath(), FileUtils.S_IRUSR
+ | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1);
+ } finally {
+ IoUtils.closeQuietly(publicZipOutStream);
+ }
}
private static void copyZipEntry(ZipEntry zipEntry,
@@ -7075,11 +7036,15 @@
}
outZipStream.putNextEntry(newEntry);
- InputStream data = inZipFile.getInputStream(zipEntry);
- while ((num = data.read(buffer)) > 0) {
- outZipStream.write(buffer, 0, num);
+ final InputStream data = inZipFile.getInputStream(zipEntry);
+ try {
+ while ((num = data.read(buffer)) > 0) {
+ outZipStream.write(buffer, 0, num);
+ }
+ outZipStream.flush();
+ } finally {
+ IoUtils.closeQuietly(data);
}
- outZipStream.flush();
}
private void deleteTempPackageFiles() {
@@ -9030,12 +8995,12 @@
}
@Override
- public void setPermissionEnforcement(String permission, int enforcement) {
+ public void setPermissionEnforced(String permission, boolean enforced) {
mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null);
if (READ_EXTERNAL_STORAGE.equals(permission)) {
synchronized (mPackages) {
- if (mSettings.mReadExternalStorageEnforcement != enforcement) {
- mSettings.mReadExternalStorageEnforcement = enforcement;
+ if (mSettings.mReadExternalStorageEnforced != enforced) {
+ mSettings.mReadExternalStorageEnforced = enforced;
mSettings.writeLPr();
// kill any non-foreground processes so we restart them and
@@ -9058,27 +9023,18 @@
}
@Override
- public int getPermissionEnforcement(String permission) {
+ public boolean isPermissionEnforced(String permission) {
mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null);
- if (READ_EXTERNAL_STORAGE.equals(permission)) {
- synchronized (mPackages) {
- return mSettings.mReadExternalStorageEnforcement;
- }
- } else {
- throw new IllegalArgumentException("No selective enforcement for " + permission);
+ synchronized (mPackages) {
+ return isPermissionEnforcedLocked(permission);
}
}
private boolean isPermissionEnforcedLocked(String permission) {
if (READ_EXTERNAL_STORAGE.equals(permission)) {
- switch (mSettings.mReadExternalStorageEnforcement) {
- case ENFORCEMENT_DEFAULT:
- return false;
- case ENFORCEMENT_YES:
- return true;
- }
+ return mSettings.mReadExternalStorageEnforced;
+ } else {
+ return true;
}
-
- return true;
}
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index bb73b29..35b6bde 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -20,7 +20,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.ENFORCEMENT_DEFAULT;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import com.android.internal.util.FastXmlSerializer;
@@ -112,7 +111,7 @@
int mInternalSdkPlatform;
int mExternalSdkPlatform;
- int mReadExternalStorageEnforcement = ENFORCEMENT_DEFAULT;
+ boolean mReadExternalStorageEnforced = PackageManager.DEFAULT_ENFORCE_READ_EXTERNAL_STORAGE;
/** Device identity for the purpose of package verification. */
private VerifierDeviceIdentity mVerifierDeviceIdentity;
@@ -1140,10 +1139,11 @@
serializer.endTag(null, "verifier");
}
- if (mReadExternalStorageEnforcement != ENFORCEMENT_DEFAULT) {
+ if (mReadExternalStorageEnforced
+ != PackageManager.DEFAULT_ENFORCE_READ_EXTERNAL_STORAGE) {
serializer.startTag(null, TAG_READ_EXTERNAL_STORAGE);
serializer.attribute(
- null, ATTR_ENFORCEMENT, Integer.toString(mReadExternalStorageEnforcement));
+ null, ATTR_ENFORCEMENT, mReadExternalStorageEnforced ? "1" : "0");
serializer.endTag(null, TAG_READ_EXTERNAL_STORAGE);
}
@@ -1548,10 +1548,7 @@
}
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
- try {
- mReadExternalStorageEnforcement = Integer.parseInt(enforcement);
- } catch (NumberFormatException e) {
- }
+ mReadExternalStorageEnforced = "1".equals(enforcement);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
@@ -2560,8 +2557,8 @@
pw.print(" perm="); pw.println(p.perm);
}
if (READ_EXTERNAL_STORAGE.equals(p.name)) {
- pw.print(" enforcement=");
- pw.println(PackageManager.enforcementToString(mReadExternalStorageEnforcement));
+ pw.print(" enforced=");
+ pw.println(mReadExternalStorageEnforced);
}
}
}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 3dcfd3c..11af6ea 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,6 +18,8 @@
import java.io.PrintWriter;
+import static com.android.server.wm.WindowStateAnimator.SurfaceTrace;
+
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
@@ -205,17 +207,23 @@
">>> OPEN TRANSACTION ScreenRotationAnimation");
Surface.openTransaction();
}
-
+
try {
try {
- mSurface = new Surface(session, 0, "FreezeSurface",
- -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ mSurface = new SurfaceTrace(session, 0, "FreezeSurface", -1, mWidth, mHeight,
+ PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
+ } else {
+ mSurface = new Surface(session, 0, "FreezeSurface", -1, mWidth, mHeight,
+ PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
+ }
if (!mSurface.isValid()) {
// Screenshot failed, punt.
mSurface = null;
return;
}
mSurface.setLayer(FREEZE_LAYER + 1);
+ mSurface.setAlpha(0);
mSurface.show();
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
@@ -308,10 +316,10 @@
if (TWO_PHASE_ANIMATION) {
return startAnimation(session, maxAnimationDuration, animationScale,
finalWidth, finalHeight, false);
- } else {
- // Don't start animation yet.
- return false;
}
+
+ // Don't start animation yet.
+ return false;
}
/**
@@ -590,29 +598,37 @@
mEnteringBlackFrame.kill();
mEnteringBlackFrame = null;
}
- if (mStartExitAnimation != null) {
- mStartExitAnimation.cancel();
- mStartExitAnimation = null;
+ if (TWO_PHASE_ANIMATION) {
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ }
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ }
+ if (mFinishEnterAnimation != null) {
+ mFinishEnterAnimation.cancel();
+ mFinishEnterAnimation = null;
+ }
}
- if (mStartEnterAnimation != null) {
- mStartEnterAnimation.cancel();
- mStartEnterAnimation = null;
- }
- if (mStartFrameAnimation != null) {
- mStartFrameAnimation.cancel();
- mStartFrameAnimation = null;
- }
- if (mFinishExitAnimation != null) {
- mFinishExitAnimation.cancel();
- mFinishExitAnimation = null;
- }
- if (mFinishEnterAnimation != null) {
- mFinishEnterAnimation.cancel();
- mFinishEnterAnimation = null;
- }
- if (mFinishFrameAnimation != null) {
- mFinishFrameAnimation.cancel();
- mFinishFrameAnimation = null;
+ if (USE_CUSTOM_BLACK_FRAME) {
+ if (mStartFrameAnimation != null) {
+ mStartFrameAnimation.cancel();
+ mStartFrameAnimation = null;
+ }
+ if (mRotateFrameAnimation != null) {
+ mRotateFrameAnimation.cancel();
+ mRotateFrameAnimation = null;
+ }
+ if (mFinishFrameAnimation != null) {
+ mFinishFrameAnimation.cancel();
+ mFinishFrameAnimation = null;
+ }
}
if (mRotateExitAnimation != null) {
mRotateExitAnimation.cancel();
@@ -622,27 +638,20 @@
mRotateEnterAnimation.cancel();
mRotateEnterAnimation = null;
}
- if (mRotateFrameAnimation != null) {
- mRotateFrameAnimation.cancel();
- mRotateFrameAnimation = null;
- }
}
public boolean isAnimating() {
- if (TWO_PHASE_ANIMATION) {
- return hasAnimations() || mFinishAnimReady;
- } else {
- return hasAnimations();
- }
+ return hasAnimations() || (TWO_PHASE_ANIMATION && mFinishAnimReady);
}
private boolean hasAnimations() {
- return mStartEnterAnimation != null || mStartExitAnimation != null
- || mStartFrameAnimation != null
- || mFinishEnterAnimation != null || mFinishExitAnimation != null
- || mFinishFrameAnimation != null
- || mRotateEnterAnimation != null || mRotateExitAnimation != null
- || mRotateFrameAnimation != null;
+ return (TWO_PHASE_ANIMATION &&
+ (mStartEnterAnimation != null || mStartExitAnimation != null
+ || mFinishEnterAnimation != null || mFinishExitAnimation != null))
+ || (USE_CUSTOM_BLACK_FRAME &&
+ (mStartFrameAnimation != null || mRotateFrameAnimation != null
+ || mFinishFrameAnimation != null))
+ || mRotateEnterAnimation != null || mRotateExitAnimation != null;
}
private boolean stepAnimation(long now) {
@@ -651,43 +660,49 @@
mFinishAnimStartTime = now;
}
- mMoreStartExit = false;
- if (mStartExitAnimation != null) {
- mMoreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
- }
+ if (TWO_PHASE_ANIMATION) {
+ mMoreStartExit = false;
+ if (mStartExitAnimation != null) {
+ mMoreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
+ }
- mMoreStartEnter = false;
- if (mStartEnterAnimation != null) {
- mMoreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
+ mMoreStartEnter = false;
+ if (mStartEnterAnimation != null) {
+ mMoreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
+ }
}
-
- mMoreStartFrame = false;
- if (mStartFrameAnimation != null) {
- mMoreStartFrame = mStartFrameAnimation.getTransformation(now, mStartFrameTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start frame: " + mStartFrameTransformation);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mMoreStartFrame = false;
+ if (mStartFrameAnimation != null) {
+ mMoreStartFrame = mStartFrameAnimation.getTransformation(now, mStartFrameTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start frame: " + mStartFrameTransformation);
+ }
}
long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
- mMoreFinishExit = false;
- if (mFinishExitAnimation != null) {
- mMoreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
- }
+ if (TWO_PHASE_ANIMATION) {
+ mMoreFinishExit = false;
+ if (mFinishExitAnimation != null) {
+ mMoreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
+ }
- mMoreFinishEnter = false;
- if (mFinishEnterAnimation != null) {
- mMoreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
+ mMoreFinishEnter = false;
+ if (mFinishEnterAnimation != null) {
+ mMoreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
+ }
}
-
- mMoreFinishFrame = false;
- if (mFinishFrameAnimation != null) {
- mMoreFinishFrame = mFinishFrameAnimation.getTransformation(finishNow, mFinishFrameTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish frame: " + mFinishFrameTransformation);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mMoreFinishFrame = false;
+ if (mFinishFrameAnimation != null) {
+ mMoreFinishFrame = mFinishFrameAnimation.getTransformation(finishNow, mFinishFrameTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish frame: " + mFinishFrameTransformation);
+ }
}
mMoreRotateExit = false;
@@ -702,24 +717,28 @@
if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
}
- mMoreRotateFrame = false;
- if (mRotateFrameAnimation != null) {
- mMoreRotateFrame = mRotateFrameAnimation.getTransformation(now, mRotateFrameTransformation);
- if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate frame: " + mRotateFrameTransformation);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mMoreRotateFrame = false;
+ if (mRotateFrameAnimation != null) {
+ mMoreRotateFrame = mRotateFrameAnimation.getTransformation(now, mRotateFrameTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate frame: " + mRotateFrameTransformation);
+ }
}
- if (!mMoreStartExit && !mMoreRotateExit && !mMoreFinishExit) {
- if (mStartExitAnimation != null) {
- if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing start exit anim!");
- mStartExitAnimation.cancel();
- mStartExitAnimation = null;
- mStartExitTransformation.clear();
- }
- if (mFinishExitAnimation != null) {
- if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing finish exit anim!");
- mFinishExitAnimation.cancel();
- mFinishExitAnimation = null;
- mFinishExitTransformation.clear();
+ if (!mMoreRotateExit && (!TWO_PHASE_ANIMATION || (!mMoreStartExit && !mMoreFinishExit))) {
+ if (TWO_PHASE_ANIMATION) {
+ if (mStartExitAnimation != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing start exit anim!");
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ mStartExitTransformation.clear();
+ }
+ if (mFinishExitAnimation != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing finish exit anim!");
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ mFinishExitTransformation.clear();
+ }
}
if (mRotateExitAnimation != null) {
if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing rotate exit anim!");
@@ -729,18 +748,20 @@
}
}
- if (!mMoreStartEnter && !mMoreRotateEnter && !mMoreFinishEnter) {
- if (mStartEnterAnimation != null) {
- if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing start enter anim!");
- mStartEnterAnimation.cancel();
- mStartEnterAnimation = null;
- mStartEnterTransformation.clear();
- }
- if (mFinishEnterAnimation != null) {
- if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing finish enter anim!");
- mFinishEnterAnimation.cancel();
- mFinishEnterAnimation = null;
- mFinishEnterTransformation.clear();
+ if (!mMoreRotateEnter && (!TWO_PHASE_ANIMATION || (!mMoreStartEnter && !mMoreFinishEnter))) {
+ if (TWO_PHASE_ANIMATION) {
+ if (mStartEnterAnimation != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing start enter anim!");
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ mStartEnterTransformation.clear();
+ }
+ if (mFinishEnterAnimation != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing finish enter anim!");
+ mFinishEnterAnimation.cancel();
+ mFinishEnterAnimation = null;
+ mFinishEnterTransformation.clear();
+ }
}
if (mRotateEnterAnimation != null) {
if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing rotate enter anim!");
@@ -772,12 +793,14 @@
}
mExitTransformation.set(mRotateExitTransformation);
- mExitTransformation.compose(mStartExitTransformation);
- mExitTransformation.compose(mFinishExitTransformation);
-
mEnterTransformation.set(mRotateEnterTransformation);
- mEnterTransformation.compose(mStartEnterTransformation);
- mEnterTransformation.compose(mFinishEnterTransformation);
+ if (TWO_PHASE_ANIMATION) {
+ mExitTransformation.compose(mStartExitTransformation);
+ mExitTransformation.compose(mFinishExitTransformation);
+
+ mEnterTransformation.compose(mStartEnterTransformation);
+ mEnterTransformation.compose(mFinishEnterTransformation);
+ }
if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
@@ -793,9 +816,11 @@
if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final frame: " + mFrameTransformation);
}
- final boolean more = mMoreStartEnter || mMoreStartExit || mMoreStartFrame
- || mMoreFinishEnter || mMoreFinishExit || mMoreFinishFrame
- || mMoreRotateEnter || mMoreRotateExit || mMoreRotateFrame
+ final boolean more = (TWO_PHASE_ANIMATION
+ && (mMoreStartEnter || mMoreStartExit || mMoreFinishEnter || mMoreFinishExit))
+ || (USE_CUSTOM_BLACK_FRAME
+ && (mMoreStartFrame || mMoreRotateFrame || mMoreFinishFrame))
+ || mMoreRotateEnter || mMoreRotateExit
|| !mFinishAnimReady;
mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
@@ -848,7 +873,7 @@
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
-
+
public boolean stepAnimationLocked(long now) {
if (!hasAnimations()) {
if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
@@ -858,23 +883,30 @@
if (!mAnimRunning) {
if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
- if (mStartEnterAnimation != null) {
- mStartEnterAnimation.setStartTime(now);
+ if (TWO_PHASE_ANIMATION) {
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.setStartTime(now);
+ }
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.setStartTime(now);
+ }
+ if (mFinishEnterAnimation != null) {
+ mFinishEnterAnimation.setStartTime(0);
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.setStartTime(0);
+ }
}
- if (mStartExitAnimation != null) {
- mStartExitAnimation.setStartTime(now);
- }
- if (mStartFrameAnimation != null) {
- mStartFrameAnimation.setStartTime(now);
- }
- if (mFinishEnterAnimation != null) {
- mFinishEnterAnimation.setStartTime(0);
- }
- if (mFinishExitAnimation != null) {
- mFinishExitAnimation.setStartTime(0);
- }
- if (mFinishFrameAnimation != null) {
- mFinishFrameAnimation.setStartTime(0);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ if (mStartFrameAnimation != null) {
+ mStartFrameAnimation.setStartTime(now);
+ }
+ if (mFinishFrameAnimation != null) {
+ mFinishFrameAnimation.setStartTime(0);
+ }
+ if (mRotateFrameAnimation != null) {
+ mRotateFrameAnimation.setStartTime(now);
+ }
}
if (mRotateEnterAnimation != null) {
mRotateEnterAnimation.setStartTime(now);
@@ -882,9 +914,6 @@
if (mRotateExitAnimation != null) {
mRotateExitAnimation.setStartTime(now);
}
- if (mRotateFrameAnimation != null) {
- mRotateFrameAnimation.setStartTime(now);
- }
mAnimRunning = true;
}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index fa009eb..7611a0f 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -23,7 +23,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashSet;
/**
* Singleton class that carries out the animations and Surface operations in a separate task
@@ -36,8 +35,7 @@
final Context mContext;
final WindowManagerPolicy mPolicy;
- HashSet<WindowStateAnimator> mWinAnimators = new HashSet<WindowStateAnimator>();
- HashSet<WindowStateAnimator> mFinished = new HashSet<WindowStateAnimator>();
+ ArrayList<WindowStateAnimator> mWinAnimators = new ArrayList<WindowStateAnimator>();
boolean mAnimating;
boolean mTokenMayBeDrawn;
@@ -433,6 +431,10 @@
mPendingLayoutChanges = 0;
mCurrentTime = SystemClock.uptimeMillis();
mBulkUpdateParams = 0;
+ mAnimating = false;
+ if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+ }
// Update animations of all applications, including those
// associated with exiting/removed apps
@@ -449,16 +451,9 @@
mScreenRotationAnimation.updateSurfaces();
}
- mFinished.clear();
- for (final WindowStateAnimator winAnimator : mWinAnimators) {
- if (winAnimator.mSurface == null) {
- mFinished.add(winAnimator);
- } else {
- winAnimator.prepareSurfaceLocked(true);
- }
- }
- for (final WindowStateAnimator winAnimator : mFinished) {
- mWinAnimators.remove(winAnimator);
+ final int N = mWinAnimators.size();
+ for (int i = 0; i < N; i++) {
+ mWinAnimators.get(i).prepareSurfaceLocked(true);
}
if (mDimParams != null) {
@@ -477,13 +472,26 @@
mService.mBlackFrame.clearMatrix();
}
}
+
+ if (mService.mWatermark != null) {
+ mService.mWatermark.drawIfNeeded();
+ }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
}
- mService.bulkSetParameters(mBulkUpdateParams);
+ mService.bulkSetParameters(mBulkUpdateParams, mPendingLayoutChanges);
+
+ if (mAnimating) {
+ mService.scheduleAnimationLocked();
+ }
+ if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ + " mPendingLayoutChanges=" + Integer.toHexString(mPendingLayoutChanges));
+ }
}
WindowState mCurrentFocus;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b0e017f..0458a67 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -50,6 +50,7 @@
import android.Manifest;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
@@ -175,6 +176,8 @@
static final boolean DEBUG_SCREENSHOT = false;
static final boolean DEBUG_BOOT = false;
static final boolean DEBUG_LAYOUT_REPEATS = true;
+ static final boolean DEBUG_SURFACE_TRACE = false;
+ static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -425,7 +428,7 @@
IInputMethodManager mInputMethodManager;
- SurfaceSession mFxSession;
+ final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
@@ -495,6 +498,7 @@
// mOpeningApps and mClosingApps are the lists of tokens that will be
// made visible or hidden at the next transition.
int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+ int mNextAppTransitionType = ActivityOptions.ANIM_NONE;
String mNextAppTransitionPackage;
Bitmap mNextAppTransitionThumbnail;
IRemoteCallback mNextAppTransitionCallback;
@@ -502,6 +506,8 @@
int mNextAppTransitionExit;
int mNextAppTransitionStartX;
int mNextAppTransitionStartY;
+ int mNextAppTransitionStartWidth;
+ int mNextAppTransitionStartHeight;
boolean mAppTransitionReady = false;
boolean mAppTransitionRunning = false;
boolean mAppTransitionTimeout = false;
@@ -592,6 +598,7 @@
static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
static final int CLEAR_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
+ static final int SET_TURN_ON_SCREEN = 1 << 4;
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
@@ -615,7 +622,20 @@
public void run() {
synchronized(mWindowMap) {
mAnimationScheduled = false;
- performLayoutAndPlaceSurfacesLocked();
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ synchronized (mAnimator) {
+ final ArrayList<WindowStateAnimator> winAnimators = mAnimator.mWinAnimators;
+ winAnimators.clear();
+ final int N = mWindows.size();
+ for (int i = 0; i < N; i++) {
+ final WindowStateAnimator winAnimator = mWindows.get(i).mWinAnimator;
+ if (winAnimator.mSurface != null) {
+ winAnimators.add(winAnimator);
+ }
+ }
+ mAnimator.animate();
+ }
}
}
}
@@ -862,6 +882,11 @@
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
+ mFxSession = new SurfaceSession();
+
+ Surface.openTransaction();
+ createWatermark();
+ Surface.closeTransaction();
}
public InputManagerService getInputManagerService() {
@@ -3051,6 +3076,50 @@
return null;
}
+ private Animation createScaleUpAnimationLocked(int transit, boolean enter) {
+ Animation a;
+ // Pick the desired duration. If this is an inter-activity transition,
+ // it is the standard duration for that. Otherwise we use the longer
+ // task transition duration.
+ int duration;
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ duration = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+ break;
+ default:
+ duration = 500;
+ break;
+ }
+ if (enter) {
+ // Entering app zooms out from the center of the initial rect.
+ Animation scale = new ScaleAnimation(
+ mNextAppTransitionStartWidth/mAppDisplayWidth, 1,
+ mNextAppTransitionStartHeight/mAppDisplayHeight, 1,
+ mNextAppTransitionStartX + mNextAppTransitionStartWidth/2,
+ mNextAppTransitionStartY + mNextAppTransitionStartHeight/2);
+ AnimationSet set = new AnimationSet(true);
+ Animation alpha = new AlphaAnimation(0, 1);
+ scale.setDuration(duration);
+ set.addAnimation(scale);
+ alpha.setDuration(duration);
+ set.addAnimation(alpha);
+ a = set;
+ } else {
+ // Exiting app just holds in place.
+ a = new AlphaAnimation(1, 1);
+ a.setDuration(duration);
+ }
+ a.setFillAfter(true);
+ final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
+ com.android.internal.R.interpolator.decelerate_quint);
+ a.setInterpolator(interpolator);
+ a.initialize(mAppDisplayWidth, mAppDisplayHeight,
+ mAppDisplayWidth, mAppDisplayHeight);
+ return a;
+ }
+
private Animation createThumbnailAnimationLocked(int transit,
boolean enter, boolean thumb) {
Animation a;
@@ -3069,7 +3138,6 @@
default:
duration = 500;
break;
-
}
if (thumb) {
// Animation for zooming thumbnail from its initial size to
@@ -3117,12 +3185,15 @@
if (okToDisplay()) {
Animation a;
boolean initialized = false;
- if (mNextAppTransitionThumbnail != null) {
- a = createThumbnailAnimationLocked(transit, enter, false);
- initialized = true;
- } else if (mNextAppTransitionPackage != null) {
+ if (mNextAppTransitionType == ActivityOptions.ANIM_CUSTOM) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
+ } else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) {
+ a = createScaleUpAnimationLocked(transit, enter);
+ initialized = true;
+ } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL) {
+ a = createThumbnailAnimationLocked(transit, enter, false);
+ initialized = true;
} else {
int animAttr = 0;
switch (transit) {
@@ -3717,6 +3788,7 @@
public void overridePendingAppTransition(String packageName,
int enterAnim, int exitAnim) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransitionType = ActivityOptions.ANIM_CUSTOM;
mNextAppTransitionPackage = packageName;
mNextAppTransitionThumbnail = null;
mNextAppTransitionEnter = enterAnim;
@@ -3724,9 +3796,23 @@
}
}
+ public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+ int startHeight) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransitionType = ActivityOptions.ANIM_SCALE_UP;
+ mNextAppTransitionPackage = null;
+ mNextAppTransitionThumbnail = null;
+ mNextAppTransitionStartX = startX;
+ mNextAppTransitionStartY = startY;
+ mNextAppTransitionStartWidth = startWidth;
+ mNextAppTransitionStartHeight = startHeight;
+ }
+ }
+
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
int startY, IRemoteCallback startedCallback) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransitionType = ActivityOptions.ANIM_THUMBNAIL;
mNextAppTransitionPackage = null;
mNextAppTransitionThumbnail = srcThumb;
mNextAppTransitionStartX = startX;
@@ -6481,6 +6567,9 @@
@Override
public void handleMessage(Message msg) {
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.v(TAG, "handleMessage: entry what=" + msg.what);
+ }
switch (msg.what) {
case REPORT_FOCUS_CHANGE: {
WindowState lastFocus;
@@ -6912,6 +7001,14 @@
doRequest = true;
}
}
+ if ((msg.arg1 & LayoutFields.SET_TURN_ON_SCREEN) != 0) {
+ mTurnOnScreen = true;
+ }
+
+ mPendingLayoutChanges |= msg.arg2;
+ if (mPendingLayoutChanges != 0) {
+ doRequest = true;
+ }
if (doRequest) {
requestTraversalLocked();
@@ -6956,6 +7053,9 @@
break;
}
}
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.v(TAG, "handleMessage: exit");
+ }
}
}
@@ -6963,6 +7063,7 @@
// IWindowManager API
// -------------------------------------------------------------
+ @Override
public IWindowSession openSession(IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
@@ -6971,6 +7072,7 @@
return session;
}
+ @Override
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
synchronized (mWindowMap) {
// The focus for the client is the window immediately below
@@ -7404,12 +7506,6 @@
} else {
mLayoutRepeatCount = 0;
}
-
- if (mAnimator.mAnimating) {
- // Do this even if requestTraversalLocked was called above so we get a frame drawn
- // at the proper time as well as the one drawn early.
- scheduleAnimationLocked();
- }
if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
@@ -7592,7 +7688,8 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Check opening app" + wtoken + ": allDrawn="
+ wtoken.allDrawn + " startingDisplayed="
- + wtoken.startingDisplayed);
+ + wtoken.startingDisplayed + " startingMoved="
+ + wtoken.startingMoved);
if (!wtoken.allDrawn && !wtoken.startingDisplayed
&& !wtoken.startingMoved) {
goodToGo = false;
@@ -7813,6 +7910,7 @@
}
}
+ mNextAppTransitionType = ActivityOptions.ANIM_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionThumbnail = null;
if (mNextAppTransitionCallback != null) {
@@ -8026,6 +8124,9 @@
// "Something has changed! Let's make it correct now."
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry");
+ }
if (mDisplay == null) {
Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
return;
@@ -8058,23 +8159,12 @@
mInnerFields.mHoldScreen = null;
mInnerFields.mScreenBrightness = -1;
mInnerFields.mButtonBrightness = -1;
- boolean focusDisplayed = false;
- mAnimator.mAnimating = false;
- boolean createWatermark = false;
-
- if (mFxSession == null) {
- mFxSession = new SurfaceSession();
- createWatermark = true;
- }
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
Surface.openTransaction();
- if (createWatermark) {
- createWatermark();
- }
if (mWatermark != null) {
mWatermark.positionSurface(dw, dh);
}
@@ -8145,6 +8235,7 @@
mInnerFields.mDimming = false;
mInnerFields.mSyswin = false;
+ boolean focusDisplayed = false;
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
WindowState w = mWindows.get(i);
@@ -8168,6 +8259,10 @@
updateWallpaperVisibilityLocked();
}
}
+ if (focusDisplayed) {
+ mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
+ }
+
if (!mInnerFields.mDimming && mAnimator.isDimming()) {
mAnimator.stopDimming();
}
@@ -8292,19 +8387,6 @@
}
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
- mAnimator.animate();
- mPendingLayoutChanges |= mAnimator.mPendingLayoutChanges;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()", mPendingLayoutChanges);
-
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
-
- if (mWatermark != null) {
- mWatermark.drawIfNeeded();
- }
-
if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete="
+ mInnerFields.mOrientationChangeComplete);
@@ -8415,9 +8497,6 @@
mToBottomApps.clear();
}
- if (focusDisplayed) {
- mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
- }
if (wallpaperDestroyed) {
mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
}
@@ -8473,9 +8552,14 @@
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
-// Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
-// + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
-// + " animating=" + mAnimator.mAnimating);
+
+ scheduleAnimationLocked();
+
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
+ + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
+ + " animating=" + mAnimator.mAnimating);
+ }
}
void checkDrawnWindowsLocked() {
@@ -8588,7 +8672,6 @@
wsa.mSurfaceShown = false;
wsa.mSurface = null;
ws.mHasSurface = false;
- mAnimator.mWinAnimators.remove(wsa);
mForceRemoves.add(ws);
i--;
N--;
@@ -8602,7 +8685,6 @@
wsa.mSurfaceShown = false;
wsa.mSurface = null;
ws.mHasSurface = false;
- mAnimator.mWinAnimators.remove(wsa);
leakedSurface = true;
}
}
@@ -8642,7 +8724,6 @@
winAnimator.mSurfaceShown = false;
winAnimator.mSurface = null;
winAnimator.mWin.mHasSurface = false;
- mAnimator.mWinAnimators.remove(winAnimator);
}
try {
@@ -8790,11 +8871,12 @@
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
-
+
mInputMonitor.freezeInputDispatchingLw();
-
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+ mNextAppTransitionType = ActivityOptions.ANIM_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionThumbnail = null;
mAppTransitionReady = true;
@@ -8851,6 +8933,7 @@
mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
scheduleAnimationLocked();
} else {
+ mAnimator.mScreenRotationAnimation.kill();
mAnimator.mScreenRotationAnimation = null;
updateRotation = true;
}
@@ -9334,20 +9417,34 @@
pw.print(Integer.toHexString(mNextAppTransition));
pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
pw.print(" mAppTransitionRunning="); pw.print(mAppTransitionRunning);
- pw.print(" mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
- if (mNextAppTransitionPackage != null) {
- pw.print(" mNextAppTransitionPackage=");
- pw.print(mNextAppTransitionPackage);
- pw.print(" mNextAppTransitionEnter=0x");
- pw.print(Integer.toHexString(mNextAppTransitionEnter));
- pw.print(" mNextAppTransitionExit=0x");
- pw.print(Integer.toHexString(mNextAppTransitionExit));
+ pw.print(" mAppTransitionTimeout="); pw.println(mAppTransitionTimeout);
+ if (mNextAppTransitionType != ActivityOptions.ANIM_NONE) {
+ pw.print(" mNextAppTransitionType="); pw.println(mNextAppTransitionType);
}
- if (mNextAppTransitionThumbnail != null) {
- pw.print(" mNextAppTransitionThumbnail=");
- pw.print(mNextAppTransitionThumbnail);
- pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
- pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+ switch (mNextAppTransitionType) {
+ case ActivityOptions.ANIM_CUSTOM:
+ pw.print(" mNextAppTransitionPackage=");
+ pw.print(mNextAppTransitionPackage);
+ pw.print(" mNextAppTransitionEnter=0x");
+ pw.print(Integer.toHexString(mNextAppTransitionEnter));
+ pw.print(" mNextAppTransitionExit=0x");
+ pw.print(Integer.toHexString(mNextAppTransitionExit));
+ break;
+ case ActivityOptions.ANIM_SCALE_UP:
+ pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+ pw.print(" mNextAppTransitionStartY=");
+ pw.println(mNextAppTransitionStartY);
+ pw.print(" mNextAppTransitionStartWidth=");
+ pw.print(mNextAppTransitionStartWidth);
+ pw.print(" mNextAppTransitionStartHeight=");
+ pw.println(mNextAppTransitionStartHeight);
+ break;
+ case ActivityOptions.ANIM_THUMBNAIL:
+ pw.print(" mNextAppTransitionThumbnail=");
+ pw.print(mNextAppTransitionThumbnail);
+ pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+ pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+ break;
}
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
@@ -9518,12 +9615,31 @@
}
}
- void bulkSetParameters(final int bulkUpdateParams) {
- mH.sendMessage(mH.obtainMessage(H.BULK_UPDATE_PARAMETERS, bulkUpdateParams, 0));
+ void bulkSetParameters(final int bulkUpdateParams, int pendingLayoutChanges) {
+ mH.sendMessage(mH.obtainMessage(H.BULK_UPDATE_PARAMETERS, bulkUpdateParams,
+ pendingLayoutChanges));
+ }
+
+ /**
+ * Never call directly. Only call through getCallers(int) or getCaller(). Otherwise
+ * the depth will be off.
+ * @param depth What level stack to return.
+ * @return A String indicating who the caller of the method that calls this is.
+ */
+ static String getCaller(int depth) {
+ StackTraceElement caller = Thread.currentThread().getStackTrace()[5 + depth];
+ return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber();
+ }
+
+ static String getCallers(final int depth) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < depth; i++) {
+ sb.append(getCaller(i)).append(" ");
+ }
+ return sb.toString();
}
static String getCaller() {
- StackTraceElement caller = Thread.currentThread().getStackTrace()[4];
- return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber();
+ return getCallers(1);
}
}
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 941a5e1..90b63a6a 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -6,15 +6,19 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.RemoteException;
import android.util.Slog;
import android.view.Surface;
+import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
@@ -25,6 +29,7 @@
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Keep track of animations and surface operations for a single WindowState.
@@ -39,6 +44,7 @@
static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
static final boolean localLOGV = WindowManagerService.localLOGV;
static final boolean DEBUG_ORIENTATION = WindowManagerService.DEBUG_ORIENTATION;
+ static final boolean DEBUG_SURFACE_TRACE = WindowManagerService.DEBUG_SURFACE_TRACE;
static final String TAG = "WindowStateAnimator";
@@ -79,9 +85,9 @@
*/
boolean mSurfaceDestroyDeferred;
- float mShownAlpha = 1;
- float mAlpha = 1;
- float mLastAlpha = 1;
+ float mShownAlpha = 0;
+ float mAlpha = 0;
+ float mLastAlpha = 0;
// Used to save animation distances between the time they are calculated and when they are
// used.
@@ -398,6 +404,116 @@
return true;
}
+ static class SurfaceTrace extends Surface {
+ private final static String SURFACE_TAG = "SurfaceTrace";
+ final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
+
+ private float mSurfaceTraceAlpha = 0;
+ private int mLayer;
+ private PointF mPosition = new PointF();
+ private Point mSize;
+ private boolean mShown = false;
+ private String mName = "Not named";
+
+ public SurfaceTrace(SurfaceSession s,
+ int pid, int display, int w, int h, int format, int flags) throws
+ OutOfResourcesException {
+ super(s, pid, display, w, h, format, flags);
+ mSize = new Point(w, h);
+ Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ public SurfaceTrace(SurfaceSession s,
+ int pid, String name, int display, int w, int h, int format, int flags)
+ throws OutOfResourcesException {
+ super(s, pid, name, display, w, h, format, flags);
+ mName = name;
+ mSize = new Point(w, h);
+ Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ mSurfaceTraceAlpha = alpha;
+ Slog.v(SURFACE_TAG, "setAlpha: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ super.setLayer(zorder);
+ mLayer = zorder;
+ Slog.v(SURFACE_TAG, "setLayer: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+
+ sSurfaces.remove(this);
+ int i;
+ for (i = sSurfaces.size() - 1; i >= 0; i--) {
+ SurfaceTrace s = sSurfaces.get(i);
+ if (s.mLayer < zorder) {
+ break;
+ }
+ }
+ sSurfaces.add(i + 1, this);
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ super.setPosition(x, y);
+ mPosition = new PointF(x, y);
+ Slog.v(SURFACE_TAG, "setPosition: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ super.setSize(w, h);
+ mSize = new Point(w, h);
+ Slog.v(SURFACE_TAG, "setSize: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+ mShown = false;
+ Slog.v(SURFACE_TAG, "hide: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+ @Override
+ public void show() {
+ super.show();
+ mShown = true;
+ Slog.v(SURFACE_TAG, "show: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
+ + WindowManagerService.getCallers(3));
+ sSurfaces.remove(this);
+ }
+
+ static void dumpAllSurfaces() {
+ final int N = sSurfaces.size();
+ for (int i = 0; i < N; i++) {
+ Slog.i(TAG, "SurfaceDump: " + sSurfaces.get(i));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Surface " + mName + ": shown=" + mShown + " layer=" + mLayer
+ + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
+ + " " + mSize.x + "x" + mSize.y;
+ }
+ }
+
Surface createSurfaceLocked() {
if (mSurface == null) {
mReportDestroySurface = false;
@@ -440,7 +556,7 @@
mSurfaceShown = false;
mSurfaceLayer = 0;
- mSurfaceAlpha = 1;
+ mSurfaceAlpha = 0;
mSurfaceX = 0;
mSurfaceY = 0;
mSurfaceW = w;
@@ -452,12 +568,18 @@
if (!PixelFormat.formatHasAlpha(attrs.format)) {
flags |= Surface.OPAQUE;
}
- mSurface = new Surface(
+ if (DEBUG_SURFACE_TRACE) {
+ mSurface = new SurfaceTrace(
+ mSession.mSurfaceSession, mSession.mPid,
+ attrs.getTitle().toString(),
+ 0, w, h, format, flags);
+ } else {
+ mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
attrs.getTitle().toString(),
0, w, h, format, flags);
+ }
mWin.mHasSurface = true;
- mAnimator.mWinAnimators.add(this);
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
" CREATE SURFACE "
+ mSurface + " IN SESSION "
@@ -468,14 +590,12 @@
+ " / " + this);
} catch (Surface.OutOfResourcesException e) {
mWin.mHasSurface = false;
- mAnimator.mWinAnimators.remove(this);
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
mDrawState = NO_SURFACE;
return null;
} catch (Exception e) {
mWin.mHasSurface = false;
- mAnimator.mWinAnimators.remove(this);
Slog.e(TAG, "Exception creating surface", e);
mDrawState = NO_SURFACE;
return null;
@@ -500,6 +620,7 @@
mSurface.setPosition(mSurfaceX, mSurfaceY);
mSurfaceLayer = mAnimLayer;
mSurface.setLayer(mAnimLayer);
+ mSurface.setAlpha(0);
mSurfaceShown = false;
mSurface.hide();
if ((mWin.mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
@@ -593,7 +714,6 @@
mSurfaceShown = false;
mSurface = null;
mWin.mHasSurface =false;
- mAnimator.mWinAnimators.remove(this);
}
}
@@ -1078,7 +1198,7 @@
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Show surface turning screen on: " + mWin);
mWin.mTurnOnScreen = false;
- mService.mTurnOnScreen = true;
+ mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
}
}
return true;
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index 85d6e11..3795074 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -59,6 +59,7 @@
static struct {
jmethodID notifyConfigurationChanged;
+ jmethodID notifyInputDevicesChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
@@ -82,6 +83,10 @@
static struct {
jclass clazz;
+} gInputDeviceClassInfo;
+
+static struct {
+ jclass clazz;
} gKeyEventClassInfo;
static struct {
@@ -179,6 +184,7 @@
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+ virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -486,6 +492,36 @@
}
}
+void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
+ JNIEnv* env = jniEnv();
+
+ size_t count = inputDevices.size();
+ jobjectArray inputDevicesObjArray = env->NewObjectArray(
+ count, gInputDeviceClassInfo.clazz, NULL);
+ if (inputDevicesObjArray) {
+ bool error = false;
+ for (size_t i = 0; i < count; i++) {
+ jobject inputDeviceObj = android_view_InputDevice_create(env, inputDevices.itemAt(i));
+ if (!inputDeviceObj) {
+ error = true;
+ break;
+ }
+
+ env->SetObjectArrayElement(inputDevicesObjArray, i, inputDeviceObj);
+ env->DeleteLocalRef(inputDeviceObj);
+ }
+
+ if (!error) {
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputDevicesChanged,
+ inputDevicesObjArray);
+ }
+
+ env->DeleteLocalRef(inputDevicesObjArray);
+ }
+
+ checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged");
+}
+
void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
int32_t switchValue, uint32_t policyFlags) {
#if DEBUG_INPUT_DISPATCHER_POLICY
@@ -1147,36 +1183,6 @@
im->setSystemUiVisibility(visibility);
}
-static jobject nativeGetInputDevice(JNIEnv* env,
- jclass clazz, jint ptr, jint deviceId) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- InputDeviceInfo deviceInfo;
- status_t status = im->getInputManager()->getReader()->getInputDeviceInfo(
- deviceId, & deviceInfo);
- if (status) {
- return NULL;
- }
-
- return android_view_InputDevice_create(env, deviceInfo);
-}
-
-static jintArray nativeGetInputDeviceIds(JNIEnv* env,
- jclass clazz, jint ptr) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- Vector<int> deviceIds;
- im->getInputManager()->getReader()->getInputDeviceIds(deviceIds);
-
- jintArray deviceIdsObj = env->NewIntArray(deviceIds.size());
- if (! deviceIdsObj) {
- return NULL;
- }
-
- env->SetIntArrayRegion(deviceIdsObj, 0, deviceIds.size(), deviceIds.array());
- return deviceIdsObj;
-}
-
static void nativeGetInputConfiguration(JNIEnv* env,
jclass clazz, jint ptr, jobject configObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1220,6 +1226,38 @@
im->setShowTouches(enabled);
}
+static void nativeVibrate(JNIEnv* env,
+ jclass clazz, jint ptr, jint deviceId, jlongArray patternObj,
+ jint repeat, jint token) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ size_t patternSize = env->GetArrayLength(patternObj);
+ if (patternSize > MAX_VIBRATE_PATTERN_SIZE) {
+ ALOGI("Skipped requested vibration because the pattern size is %d "
+ "which is more than the maximum supported size of %d.",
+ patternSize, MAX_VIBRATE_PATTERN_SIZE);
+ return; // limit to reasonable size
+ }
+
+ jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
+ patternObj, NULL));
+ nsecs_t pattern[patternSize];
+ for (size_t i = 0; i < patternSize; i++) {
+ pattern[i] = max(jlong(0), min(patternMillis[i],
+ MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL)) * 1000000LL;
+ }
+ env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
+
+ im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
+}
+
+static void nativeCancelVibrate(JNIEnv* env,
+ jclass clazz, jint ptr, jint deviceId, jint token) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
+}
+
static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1273,10 +1311,6 @@
(void*) nativeSetInputDispatchMode },
{ "nativeSetSystemUiVisibility", "(II)V",
(void*) nativeSetSystemUiVisibility },
- { "nativeGetInputDevice", "(II)Landroid/view/InputDevice;",
- (void*) nativeGetInputDevice },
- { "nativeGetInputDeviceIds", "(I)[I",
- (void*) nativeGetInputDeviceIds },
{ "nativeGetInputConfiguration", "(ILandroid/content/res/Configuration;)V",
(void*) nativeGetInputConfiguration },
{ "nativeTransferTouchFocus", "(ILandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
@@ -1285,6 +1319,10 @@
(void*) nativeSetPointerSpeed },
{ "nativeSetShowTouches", "(IZ)V",
(void*) nativeSetShowTouches },
+ { "nativeVibrate", "(II[JII)V",
+ (void*) nativeVibrate },
+ { "nativeCancelVibrate", "(III)V",
+ (void*) nativeCancelVibrate },
{ "nativeDump", "(I)Ljava/lang/String;",
(void*) nativeDump },
{ "nativeMonitor", "(I)V",
@@ -1316,6 +1354,9 @@
GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
+ GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz,
+ "notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V");
+
GET_METHOD_ID(gServiceClassInfo.notifyLidSwitchChanged, clazz,
"notifyLidSwitchChanged", "(JZ)V");
@@ -1377,6 +1418,11 @@
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ // InputDevice
+
+ FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
+ gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
+
// KeyEvent
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 8cfdb79..183beb1 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -64,8 +64,8 @@
/*
* Calling Line Identification Restriction (CLIR)
*/
- private static final String CLIR_ON = "*31#+";
- private static final String CLIR_OFF = "#31#+";
+ private static final String CLIR_ON = "*31#";
+ private static final String CLIR_OFF = "#31#";
/*
* TOA = TON + NPI
@@ -213,23 +213,26 @@
int len = phoneNumber.length();
StringBuilder ret = new StringBuilder(len);
- boolean firstCharAdded = false;
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
- if (isDialable(c) && (c != '+' || !firstCharAdded)) {
- firstCharAdded = true;
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ ret.append(digit);
+ } else if (c == '+') {
+ // Allow '+' as first character or after CLIR MMI prefix
+ String prefix = ret.toString();
+ if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
+ ret.append(c);
+ }
+ } else if (isDialable(c)) {
ret.append(c);
} else if (isStartsPostDial (c)) {
break;
}
}
- int pos = addPlusChar(phoneNumber);
- if (pos >= 0 && ret.length() > pos) {
- ret.insert(pos, '+');
- }
-
return ret.toString();
}
@@ -283,7 +286,11 @@
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
- if (isNonSeparator(c)) {
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ ret.append(digit);
+ } else if (isNonSeparator(c)) {
ret.append(c);
}
}
@@ -371,28 +378,6 @@
}
}
- /** GSM codes
- * Finds if a GSM code includes the international prefix (+).
- *
- * @param number the number to dial.
- *
- * @return the position where the + char will be inserted, -1 if the GSM code was not found.
- */
- private static int
- addPlusChar(String number) {
- int pos = -1;
-
- if (number.startsWith(CLIR_OFF)) {
- pos = CLIR_OFF.length() - 1;
- }
-
- if (number.startsWith(CLIR_ON)) {
- pos = CLIR_ON.length() - 1;
- }
-
- return pos;
- }
-
/**
* Extracts the post-dial sequence of DTMF control digits, pauses, and
* waits. Strips separators. This string may be empty, but will not be null
@@ -1504,7 +1489,11 @@
int len = phoneNumber.length();
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
- if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) {
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ sb.append(digit);
+ } else if (i == 0 && c == '+') {
sb.append(c);
} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
@@ -1513,6 +1502,27 @@
return sb.toString();
}
+ /**
+ * Replace arabic/unicode digits with decimal digits.
+ * @param number
+ * the number to be normalized.
+ * @return the replaced number.
+ *
+ * @hide
+ */
+ public static String replaceUnicodeDigits(String number) {
+ StringBuilder normalizedDigits = new StringBuilder(number.length());
+ for (char c : number.toCharArray()) {
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ normalizedDigits.append(digit);
+ } else {
+ normalizedDigits.append(c);
+ }
+ }
+ return normalizedDigits.toString();
+ }
+
// Three and four digit phone numbers for either special services,
// or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
// not match.
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 486a924..a124c7f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -35,6 +35,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
@@ -208,6 +209,27 @@
protected static final int EVENT_RIL_CONNECTED = BASE + 5;
protected static final int EVENT_DISCONNECT_ALL = BASE + 6;
+ private static final int CMD_TO_STRING_COUNT = EVENT_DISCONNECT_ALL + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
+ sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
+ "EVENT_SETUP_DATA_CONNECTION_DONE";
+ sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
+ sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
+ sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
+ sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
+ sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
+ }
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return null;
+ }
+ }
+
//***** Tag IDs for EventLog
protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
@@ -242,6 +264,7 @@
protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
DataConnectionTracker dct) {
super(name);
+ setProcessedMessagesSize(100);
if (DBG) log("DataConnection constructor E");
this.phone = phone;
this.mDataConnectionTracker = dct;
@@ -1188,26 +1211,65 @@
new DisconnectParams(reason, onCompletedMsg)));
}
+ /**
+ * @return the string for msg.what as our info.
+ */
+ @Override
+ protected String getMessageInfo(Message msg) {
+ String info = null;
+ info = cmdToString(msg.what);
+ if (info == null) {
+ info = DataConnectionAc.cmdToString(msg.what);
+ }
+ return info;
+ }
+
+ /**
+ * Convert a System.currentTimeMillis() value to a time of day value.
+ *
+ * @param millis since the epoch (1/1/1970)
+ * @return String representation of the time.
+ */
+ private String timeMillisToTimeOfDay(long millis) {
+ Calendar c = Calendar.getInstance();
+ if (millis >= 0) {
+ c.setTimeInMillis(millis);
+ return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+ } else {
+ return Long.toString(millis);
+ }
+ }
+ /**
+ * Dump the current state.
+ *
+ * @param fd
+ * @param pw
+ * @param args
+ */
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("DataConnection name=" + getName() + ":");
+ pw.print("DataConnection ");
+ super.dump(fd, pw, args);
pw.println(" mApnList=" + mApnList);
+ pw.flush();
pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
pw.println(" mApn=" + mApn);
pw.println(" mTag=" + mTag);
+ pw.flush();
pw.println(" phone=" + phone);
pw.println(" mRilVersion=" + mRilVersion);
pw.println(" cid=" + cid);
+ pw.flush();
pw.println(" mLinkProperties=" + mLinkProperties);
+ pw.flush();
pw.println(" mCapabilities=" + mCapabilities);
- pw.println(" createTime=" + createTime);
- pw.println(" lastFailTime=" + lastFailTime);
+ pw.println(" createTime=" + timeMillisToTimeOfDay(createTime));
+ pw.println(" lastFailTime=" + timeMillisToTimeOfDay(lastFailTime));
pw.println(" lastFailCause=" + lastFailCause);
+ pw.flush();
pw.println(" mRetryOverride=" + mRetryOverride);
pw.println(" mRefCount=" + mRefCount);
pw.println(" userData=" + userData);
- pw.println(" total messages=" + getProcessedMessagesCount());
- for (int i=0; i < getProcessedMessagesSize(); i++) {
- pw.printf(" msg[%d]=%s\n", i, getProcessedMessageInfo(i));
- }
+ pw.flush();
}
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index a9f2cd1..4744ff0 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -82,6 +82,51 @@
public static final int REQ_GET_RECONNECT_INTENT = BASE + 26;
public static final int RSP_GET_RECONNECT_INTENT = BASE + 27;
+ private static final int CMD_TO_STRING_COUNT = RSP_GET_RECONNECT_INTENT + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[REQ_IS_INACTIVE - BASE] = "REQ_IS_INACTIVE";
+ sCmdToString[RSP_IS_INACTIVE - BASE] = "RSP_IS_INACTIVE";
+ sCmdToString[REQ_GET_CID - BASE] = "REQ_GET_CID";
+ sCmdToString[RSP_GET_CID - BASE] = "RSP_GET_CID";
+ sCmdToString[REQ_GET_APNSETTING - BASE] = "REQ_GET_APNSETTING";
+ sCmdToString[RSP_GET_APNSETTING - BASE] = "RSP_GET_APNSETTING";
+ sCmdToString[REQ_GET_LINK_PROPERTIES - BASE] = "REQ_GET_LINK_PROPERTIES";
+ sCmdToString[RSP_GET_LINK_PROPERTIES - BASE] = "RSP_GET_LINK_PROPERTIES";
+ sCmdToString[REQ_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+ "REQ_SET_LINK_PROPERTIES_HTTP_PROXY";
+ sCmdToString[RSP_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+ "RSP_SET_LINK_PROPERTIES_HTTP_PROXY";
+ sCmdToString[REQ_GET_LINK_CAPABILITIES - BASE] = "REQ_GET_LINK_CAPABILITIES";
+ sCmdToString[RSP_GET_LINK_CAPABILITIES - BASE] = "RSP_GET_LINK_CAPABILITIES";
+ sCmdToString[REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+ "REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+ sCmdToString[RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+ "RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+ sCmdToString[REQ_RESET - BASE] = "REQ_RESET";
+ sCmdToString[RSP_RESET - BASE] = "RSP_RESET";
+ sCmdToString[REQ_GET_REFCOUNT - BASE] = "REQ_GET_REFCOUNT";
+ sCmdToString[RSP_GET_REFCOUNT - BASE] = "RSP_GET_REFCOUNT";
+ sCmdToString[REQ_ADD_APNCONTEXT - BASE] = "REQ_ADD_APNCONTEXT";
+ sCmdToString[RSP_ADD_APNCONTEXT - BASE] = "RSP_ADD_APNCONTEXT";
+ sCmdToString[REQ_REMOVE_APNCONTEXT - BASE] = "REQ_REMOVE_APNCONTEXT";
+ sCmdToString[RSP_REMOVE_APNCONTEXT - BASE] = "RSP_REMOVE_APNCONTEXT";
+ sCmdToString[REQ_GET_APNCONTEXT_LIST - BASE] = "REQ_GET_APNCONTEXT_LIST";
+ sCmdToString[RSP_GET_APNCONTEXT_LIST - BASE] = "RSP_GET_APNCONTEXT_LIST";
+ sCmdToString[REQ_SET_RECONNECT_INTENT - BASE] = "REQ_SET_RECONNECT_INTENT";
+ sCmdToString[RSP_SET_RECONNECT_INTENT - BASE] = "RSP_SET_RECONNECT_INTENT";
+ sCmdToString[REQ_GET_RECONNECT_INTENT - BASE] = "REQ_GET_RECONNECT_INTENT";
+ sCmdToString[RSP_GET_RECONNECT_INTENT - BASE] = "RSP_GET_RECONNECT_INTENT";
+ }
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return AsyncChannel.cmdToString(cmd + BASE);
+ }
+ }
+
/**
* enum used to notify action taken or necessary to be
* taken after the link property is changed.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 1b4cb15..2ac9365 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -250,7 +250,7 @@
// Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
mSmsStorageMonitor = new SmsStorageMonitor(this);
- mSmsUsageMonitor = new SmsUsageMonitor(context.getContentResolver());
+ mSmsUsageMonitor = new SmsUsageMonitor(context);
}
public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index d6f96ff..28b729d 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -17,34 +17,40 @@
package com.android.internal.telephony;
import android.app.Activity;
-import android.app.PendingIntent;
import android.app.AlertDialog;
+import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.content.Intent;
import android.content.DialogInterface;
-import android.content.IntentFilter;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
-import android.provider.Settings;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
import android.telephony.SmsCbMessage;
import android.telephony.SmsMessage;
-import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.Spanned;
import android.util.Log;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import com.android.internal.util.HexDump;
@@ -54,22 +60,17 @@
import java.util.HashMap;
import java.util.Random;
-import com.android.internal.R;
-
+import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
-import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
-import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
public abstract class SMSDispatcher extends Handler {
static final String TAG = "SMS"; // accessed from inner class
private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
- /** Default timeout for SMS sent query */
- private static final int DEFAULT_SMS_TIMEOUT = 6000;
-
/** Permission required to receive SMS and SMS-CB messages. */
public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS";
@@ -102,23 +103,27 @@
/** Retry sending a previously failed SMS message */
private static final int EVENT_SEND_RETRY = 3;
- /** SMS confirm required */
- private static final int EVENT_POST_ALERT = 4;
+ /** Confirmation required for sending a large number of messages. */
+ private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
/** Send the user confirmed SMS */
static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class
- /** Alert is timeout */
- private static final int EVENT_ALERT_TIMEOUT = 6;
-
- /** Stop the sending */
+ /** Don't send SMS (user did not confirm). */
static final int EVENT_STOP_SENDING = 7; // accessed from inner class
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
+
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
+
protected final Phone mPhone;
protected final Context mContext;
protected final ContentResolver mResolver;
protected final CommandsInterface mCm;
protected final SmsStorageMonitor mStorageMonitor;
+ protected final TelephonyManager mTelephonyManager;
protected final WapPushOverSms mWapPush;
@@ -144,7 +149,8 @@
/** Outgoing message counter. Shared by all dispatchers. */
private final SmsUsageMonitor mUsageMonitor;
- private final ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
+ /** Number of outgoing SmsTrackers waiting for user confirmation. */
+ private int mPendingTrackerCount;
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
@@ -182,6 +188,7 @@
mCm = phone.mCM;
mStorageMonitor = storageMonitor;
mUsageMonitor = usageMonitor;
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
createWakelock();
@@ -279,51 +286,44 @@
sendSms((SmsTracker) msg.obj);
break;
- case EVENT_POST_ALERT:
+ case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
handleReachSentLimit((SmsTracker)(msg.obj));
break;
- case EVENT_ALERT_TIMEOUT:
- ((AlertDialog)(msg.obj)).dismiss();
- msg.obj = null;
- if (mSTrackers.isEmpty() == false) {
- try {
- SmsTracker sTracker = mSTrackers.remove(0);
- sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
- } catch (CanceledException ex) {
- Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
- }
- }
- if (false) {
- Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending");
- }
+ case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(false, (SmsTracker)(msg.obj));
+ break;
+
+ case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(true, (SmsTracker)(msg.obj));
break;
case EVENT_SEND_CONFIRMED_SMS:
- if (mSTrackers.isEmpty() == false) {
- SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
- if (sTracker.isMultipart()) {
- sendMultipartSms(sTracker);
- } else {
- sendSms(sTracker);
- }
- removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
+ {
+ SmsTracker tracker = (SmsTracker) msg.obj;
+ if (tracker.isMultipart()) {
+ sendMultipartSms(tracker);
+ } else {
+ sendSms(tracker);
}
+ mPendingTrackerCount--;
break;
+ }
case EVENT_STOP_SENDING:
- if (mSTrackers.isEmpty() == false) {
- // Remove the latest one.
+ {
+ SmsTracker tracker = (SmsTracker) msg.obj;
+ if (tracker.mSentIntent != null) {
try {
- SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
- sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
+ tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
} catch (CanceledException ex) {
- Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
+ Log.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED");
}
- removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
}
+ mPendingTrackerCount--;
break;
}
+ }
}
private void createWakelock() {
@@ -397,7 +397,7 @@
int ss = mPhone.getServiceState().getState();
if (ss != ServiceState.STATE_IN_SERVICE) {
- handleNotInService(ss, tracker);
+ handleNotInService(ss, tracker.mSentIntent);
} else if ((((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.SMS_FAIL_RETRY) &&
tracker.mRetryCount < MAX_SEND_RETRIES) {
@@ -446,15 +446,15 @@
* OUT_OF_SERVICE
* EMERGENCY_ONLY
* POWER_OFF
- * @param tracker An SmsTracker for the current message.
+ * @param sentIntent the PendingIntent to send the error to
*/
- protected static void handleNotInService(int ss, SmsTracker tracker) {
- if (tracker.mSentIntent != null) {
+ protected static void handleNotInService(int ss, PendingIntent sentIntent) {
+ if (sentIntent != null) {
try {
if (ss == ServiceState.STATE_POWER_OFF) {
- tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF);
+ sentIntent.send(RESULT_ERROR_RADIO_OFF);
} else {
- tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE);
+ sentIntent.send(RESULT_ERROR_NO_SERVICE);
}
} catch (CanceledException ex) {}
}
@@ -865,9 +865,10 @@
* @param deliveryIntent if not NULL this <code>Intent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
+ * @param destAddr the destination phone number (for short code confirmation)
*/
protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
+ PendingIntent deliveryIntent, String destAddr) {
if (mSmsSendDisabled) {
if (sentIntent != null) {
try {
@@ -891,61 +892,190 @@
map.put("smsc", smsc);
map.put("pdu", pdu);
- SmsTracker tracker = new SmsTracker(map, sentIntent,
- deliveryIntent);
- int ss = mPhone.getServiceState().getState();
+ // Get calling app package name via UID from Binder call
+ PackageManager pm = mContext.getPackageManager();
+ String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
- if (ss != ServiceState.STATE_IN_SERVICE) {
- handleNotInService(ss, tracker);
- } else {
- String appName = getAppNameByIntent(sentIntent);
- if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
- sendSms(tracker);
+ if (packageNames == null || packageNames.length == 0) {
+ // Refuse to send SMS if we can't get the calling package name.
+ Log.e(TAG, "Can't get calling app package name: refusing to send SMS");
+ if (sentIntent != null) {
+ try {
+ sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ } catch (CanceledException ex) {
+ Log.e(TAG, "failed to send error result");
+ }
+ }
+ return;
+ }
+
+ String appPackage = packageNames[0];
+
+ // Strip non-digits from destination phone number before checking for short codes
+ // and before displaying the number to the user if confirmation is required.
+ SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appPackage,
+ PhoneNumberUtils.extractNetworkPortion(destAddr));
+
+ // checkDestination() returns true if the destination is not a premium short code or the
+ // sending app is approved to send to short codes. Otherwise, a message is sent to our
+ // handler with the SmsTracker to request user confirmation before sending.
+ if (checkDestination(tracker)) {
+ // check for excessive outgoing SMS usage by this app
+ if (!mUsageMonitor.check(appPackage, SINGLE_PART_SMS)) {
+ sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
+ return;
+ }
+
+ int ss = mPhone.getServiceState().getState();
+
+ if (ss != ServiceState.STATE_IN_SERVICE) {
+ handleNotInService(ss, tracker.mSentIntent);
} else {
- sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
+ sendSms(tracker);
}
}
}
/**
- * Post an alert while SMS needs user confirm.
+ * Check if destination is a potential premium short code and sender is not pre-approved to
+ * send to short codes.
*
- * An SmsTracker for the current message.
+ * @param tracker the tracker for the SMS to send
+ * @return true if the destination is approved; false if user confirmation event was sent
*/
- protected void handleReachSentLimit(SmsTracker tracker) {
- if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) {
- // Deny the sending when the queue limit is reached.
+ boolean checkDestination(SmsTracker tracker) {
+ if (mUsageMonitor.isApprovedShortCodeSender(tracker.mAppPackage)) {
+ return true; // app is pre-approved to send to short codes
+ } else {
+ String countryIso = mTelephonyManager.getSimCountryIso();
+ if (countryIso == null || countryIso.length() != 2) {
+ Log.e(TAG, "Can't get SIM country code: trying network country code");
+ countryIso = mTelephonyManager.getNetworkCountryIso();
+ }
+
+ switch (mUsageMonitor.checkDestination(tracker.mDestAddress, countryIso)) {
+ case SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE:
+ sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE,
+ tracker));
+ return false; // wait for user confirmation before sending
+
+ case SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE:
+ sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE,
+ tracker));
+ return false; // wait for user confirmation before sending
+
+ case SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE:
+ case SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE:
+ case SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE:
+ default:
+ return true; // destination is not a premium short code
+ }
+ }
+ }
+
+ /**
+ * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
+ * must be confirmed by the user due to excessive usage or potential premium SMS detected.
+ * @param tracker the SmsTracker for the message to send
+ * @return true if the message was denied; false to continue with send confirmation
+ */
+ private boolean denyIfQueueLimitReached(SmsTracker tracker) {
+ if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
+ // Deny sending message when the queue limit is reached.
try {
tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
}
- return;
+ return true;
+ }
+ mPendingTrackerCount++;
+ return false;
+ }
+
+ /**
+ * Returns the label for the specified app package name.
+ * @param appPackage the package name of the app requesting to send an SMS
+ * @return the label for the specified app, or the package name if getApplicationInfo() fails
+ */
+ private CharSequence getAppLabel(String appPackage) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
+ return appInfo.loadLabel(pm);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "PackageManager Name Not Found for package " + appPackage);
+ return appPackage; // fall back to package name if we can't get app label
+ }
+ }
+
+ /**
+ * Post an alert when SMS needs confirmation due to excessive usage.
+ * @param tracker an SmsTracker for the current message.
+ */
+ protected void handleReachSentLimit(SmsTracker tracker) {
+ if (denyIfQueueLimitReached(tracker)) {
+ return; // queue limit reached; error was returned to caller
}
+ CharSequence appLabel = getAppLabel(tracker.mAppPackage);
Resources r = Resources.getSystem();
+ Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
- String appName = getAppNameByIntent(tracker.mSentIntent);
+ ConfirmDialogListener listener = new ConfirmDialogListener(tracker);
AlertDialog d = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.sms_control_title))
- .setMessage(appName + " " + r.getString(R.string.sms_control_message))
- .setPositiveButton(r.getString(R.string.sms_control_yes), mListener)
- .setNegativeButton(r.getString(R.string.sms_control_no), mListener)
+ .setTitle(R.string.sms_control_title)
+ .setIcon(R.drawable.stat_sys_warning)
+ .setMessage(messageText)
+ .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
+ .setNegativeButton(r.getString(R.string.sms_control_no), listener)
+ .setOnCancelListener(listener)
.create();
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.show();
-
- mSTrackers.add(tracker);
- sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
- DEFAULT_SMS_TIMEOUT);
}
- protected static String getAppNameByIntent(PendingIntent intent) {
+ /**
+ * Post an alert for user confirmation when sending to a potential short code.
+ * @param isPremium true if the destination is known to be a premium short code
+ * @param tracker the SmsTracker for the current message.
+ */
+ protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
+ if (denyIfQueueLimitReached(tracker)) {
+ return; // queue limit reached; error was returned to caller
+ }
+
+ int messageId;
+ int titleId;
+ if (isPremium) {
+ messageId = R.string.sms_premium_short_code_confirm_message;
+ titleId = R.string.sms_premium_short_code_confirm_title;
+ } else {
+ messageId = R.string.sms_short_code_confirm_message;
+ titleId = R.string.sms_short_code_confirm_title;
+ }
+
+ CharSequence appLabel = getAppLabel(tracker.mAppPackage);
Resources r = Resources.getSystem();
- return (intent != null) ? intent.getTargetPackage()
- : r.getString(R.string.sms_control_default_app_name);
+ Spanned messageText = Html.fromHtml(r.getString(messageId, appLabel, tracker.mDestAddress));
+
+ ConfirmDialogListener listener = new ConfirmDialogListener(tracker);
+
+ AlertDialog d = new AlertDialog.Builder(mContext)
+ .setTitle(titleId)
+ .setIcon(R.drawable.stat_sys_warning)
+ .setMessage(messageText)
+ .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
+ .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
+// TODO: add third button for "Report malicious app" feature
+// .setNeutralButton(r.getString(R.string.sms_short_code_confirm_report), listener)
+ .setOnCancelListener(listener)
+ .create();
+
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
}
/**
@@ -982,7 +1112,7 @@
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
- handleNotInService(ss, new SmsTracker(null, sentIntent, null));
+ handleNotInService(ss, sentIntent);
}
return;
}
@@ -1032,12 +1162,17 @@
public final PendingIntent mSentIntent;
public final PendingIntent mDeliveryIntent;
+ public final String mAppPackage;
+ public final String mDestAddress;
+
public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
+ PendingIntent deliveryIntent, String appPackage, String destAddr) {
mData = data;
mSentIntent = sentIntent;
mDeliveryIntent = deliveryIntent;
mRetryCount = 0;
+ mAppPackage = appPackage;
+ mDestAddress = destAddr;
}
/**
@@ -1050,19 +1185,35 @@
}
}
- private final DialogInterface.OnClickListener mListener =
- new DialogInterface.OnClickListener() {
+ /**
+ * Dialog listener for SMS confirmation dialog.
+ */
+ private final class ConfirmDialogListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- Log.d(TAG, "click YES to send out sms");
- sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS));
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- Log.d(TAG, "click NO to stop sending");
- sendMessage(obtainMessage(EVENT_STOP_SENDING));
- }
+ private final SmsTracker mTracker;
+
+ ConfirmDialogListener(SmsTracker tracker) {
+ mTracker = tracker;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Log.d(TAG, "CONFIRM sending SMS");
+ sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ Log.d(TAG, "DENY sending SMS");
+ sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
}
- };
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ Log.d(TAG, "dialog dismissed: don't send SMS");
+ sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+ }
+ }
private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
@Override
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
index bd2ae8b..07a0a28 100644
--- a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -17,13 +17,23 @@
package com.android.internal.telephony;
import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
import android.provider.Settings;
+import android.telephony.PhoneNumberUtils;
import android.util.Log;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Implement the per-application based SMS control, which limits the number of
@@ -34,13 +44,28 @@
* dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
*/
public class SmsUsageMonitor {
- private static final String TAG = "SmsStorageMonitor";
+ private static final String TAG = "SmsUsageMonitor";
/** Default checking period for SMS sent without user permission. */
- private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
+ private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000; // 30 minutes
/** Default number of SMS sent in checking period without user permission. */
- private static final int DEFAULT_SMS_MAX_COUNT = 100;
+ private static final int DEFAULT_SMS_MAX_COUNT = 30;
+
+ /** Return value from {@link #checkDestination} for regular phone numbers. */
+ static final int CATEGORY_NOT_SHORT_CODE = 0;
+
+ /** Return value from {@link #checkDestination} for free (no cost) short codes. */
+ static final int CATEGORY_FREE_SHORT_CODE = 1;
+
+ /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */
+ static final int CATEGORY_STANDARD_SHORT_CODE = 2;
+
+ /** Return value from {@link #checkDestination} for possible premium short codes. */
+ static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+
+ /** Return value from {@link #checkDestination} for premium short codes. */
+ static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
private final int mCheckPeriod;
private final int mMaxAllowed;
@@ -48,10 +73,89 @@
new HashMap<String, ArrayList<Long>>();
/**
- * Create SMS usage monitor.
- * @param resolver the ContentResolver to use to load from secure settings
+ * Hash of package names that are allowed to send to short codes.
+ * TODO: persist this across reboots.
*/
- public SmsUsageMonitor(ContentResolver resolver) {
+ private final HashSet<String> mApprovedShortCodeSenders = new HashSet<String>();
+
+ /** Context for retrieving regexes from XML resource. */
+ private final Context mContext;
+
+ /** Country code for the cached short code pattern matcher. */
+ private String mCurrentCountry;
+
+ /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
+ private ShortCodePatternMatcher mCurrentPatternMatcher;
+
+ /** XML tag for root element. */
+ private static final String TAG_SHORTCODES = "shortcodes";
+
+ /** XML tag for short code patterns for a specific country. */
+ private static final String TAG_SHORTCODE = "shortcode";
+
+ /** XML attribute for the country code. */
+ private static final String ATTR_COUNTRY = "country";
+
+ /** XML attribute for the short code regex pattern. */
+ private static final String ATTR_PATTERN = "pattern";
+
+ /** XML attribute for the premium short code regex pattern. */
+ private static final String ATTR_PREMIUM = "premium";
+
+ /** XML attribute for the free short code regex pattern. */
+ private static final String ATTR_FREE = "free";
+
+ /** XML attribute for the standard rate short code regex pattern. */
+ private static final String ATTR_STANDARD = "standard";
+
+ /**
+ * SMS short code regex pattern matcher for a specific country.
+ */
+ private static final class ShortCodePatternMatcher {
+ private final Pattern mShortCodePattern;
+ private final Pattern mPremiumShortCodePattern;
+ private final Pattern mFreeShortCodePattern;
+ private final Pattern mStandardShortCodePattern;
+
+ ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex,
+ String freeShortCodeRegex, String standardShortCodeRegex) {
+ mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null);
+ mPremiumShortCodePattern = (premiumShortCodeRegex != null ?
+ Pattern.compile(premiumShortCodeRegex) : null);
+ mFreeShortCodePattern = (freeShortCodeRegex != null ?
+ Pattern.compile(freeShortCodeRegex) : null);
+ mStandardShortCodePattern = (standardShortCodeRegex != null ?
+ Pattern.compile(standardShortCodeRegex) : null);
+ }
+
+ int getNumberCategory(String phoneNumber) {
+ if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_FREE_SHORT_CODE;
+ }
+ if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_STANDARD_SHORT_CODE;
+ }
+ if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_PREMIUM_SHORT_CODE;
+ }
+ if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) {
+ return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+ }
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+ }
+
+ /**
+ * Create SMS usage monitor.
+ * @param context the context to use to load resources and get TelephonyManager service
+ */
+ public SmsUsageMonitor(Context context) {
+ mContext = context;
+ ContentResolver resolver = context.getContentResolver();
+
mMaxAllowed = Settings.Secure.getInt(resolver,
Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT,
DEFAULT_SMS_MAX_COUNT);
@@ -59,6 +163,50 @@
mCheckPeriod = Settings.Secure.getInt(resolver,
Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
DEFAULT_SMS_CHECK_PERIOD);
+
+ // system MMS app is always allowed to send to short codes
+ mApprovedShortCodeSenders.add("com.android.mms");
+ }
+
+ /**
+ * Return a pattern matcher object for the specified country.
+ * @param country the country to search for
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private ShortCodePatternMatcher getPatternMatcher(String country) {
+ int id = com.android.internal.R.xml.sms_short_codes;
+ XmlResourceParser parser = mContext.getResources().getXml(id);
+
+ try {
+ XmlUtils.beginDocument(parser, TAG_SHORTCODES);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (element.equals(TAG_SHORTCODE)) {
+ String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
+ if (country.equals(currentCountry)) {
+ String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
+ String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
+ String free = parser.getAttributeValue(null, ATTR_FREE);
+ String standard = parser.getAttributeValue(null, ATTR_STANDARD);
+ return new ShortCodePatternMatcher(pattern, premium, free, standard);
+ }
+ } else {
+ Log.e(TAG, "Error: skipping unknown XML tag " + element);
+ }
+ }
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "XML parser exception reading short code pattern resource", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O exception reading short code pattern resource", e);
+ } finally {
+ parser.close();
+ }
+ return null; // country not found
}
/** Clear the SMS application list for disposal. */
@@ -67,9 +215,11 @@
}
/**
- * Check to see if an application is allowed to send new SMS messages.
+ * Check to see if an application is allowed to send new SMS messages, and confirm with
+ * user if the send limit was reached or if a non-system app is potentially sending to a
+ * premium SMS short code or number.
*
- * @param appName the application sending sms
+ * @param appName the package name of the app requesting to send an SMS
* @param smsWaiting the number of new messages desired to send
* @return true if application is allowed to send the requested number
* of new sms messages
@@ -89,6 +239,69 @@
}
/**
+ * Return whether the app is approved to send to any short code.
+ * @param appName the package name of the app requesting to send an SMS
+ * @return true if the app is approved; false if we need to confirm short code destinations
+ */
+ public boolean isApprovedShortCodeSender(String appName) {
+ return mApprovedShortCodeSenders.contains(appName);
+ }
+
+ /**
+ * Add app package name to the list of approved short code senders.
+ * @param appName the package name of the app to add
+ */
+ public void addApprovedShortCodeSender(String appName) {
+ Log.d(TAG, "Adding " + appName + " to list of approved short code senders.");
+ mApprovedShortCodeSenders.add(appName);
+ }
+
+ /**
+ * Check if the destination is a possible premium short code.
+ * NOTE: the caller is expected to strip non-digits from the destination number with
+ * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
+ * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number
+ * for testing and in the user confirmation dialog if the user needs to confirm the number.
+ * This makes it difficult for malware to fool the user or the short code pattern matcher
+ * by using non-ASCII characters to make the number appear to be different from the real
+ * destination phone number.
+ *
+ * @param destAddress the destination address to test for possible short code
+ * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE},
+ * {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
+ */
+ public int checkDestination(String destAddress, String countryIso) {
+ // always allow emergency numbers
+ if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+
+ ShortCodePatternMatcher patternMatcher = null;
+
+ if (countryIso != null) {
+ if (countryIso.equals(mCurrentCountry)) {
+ patternMatcher = mCurrentPatternMatcher;
+ } else {
+ patternMatcher = getPatternMatcher(countryIso);
+ mCurrentCountry = countryIso;
+ mCurrentPatternMatcher = patternMatcher; // may be null if not found
+ }
+ }
+
+ if (patternMatcher != null) {
+ return patternMatcher.getNumberCategory(destAddress);
+ } else {
+ // Generic rule: numbers of 5 digits or less are considered potential short codes
+ Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
+ if (destAddress.length() <= 5) {
+ return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+ } else {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+ }
+ }
+
+ /**
* Remove keys containing only old timestamps. This can happen if an SMS app is used
* to send messages and then uninstalled.
*/
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index bfa23e7..e6b45f6 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -283,7 +283,7 @@
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, destPort, data, (deliveryIntent != null));
- sendSubmitPdu(pdu, sentIntent, deliveryIntent);
+ sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr);
}
/** {@inheritDoc} */
@@ -292,7 +292,7 @@
PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null), null);
- sendSubmitPdu(pdu, sentIntent, deliveryIntent);
+ sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr);
}
/** {@inheritDoc} */
@@ -324,11 +324,11 @@
SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress,
uData, (deliveryIntent != null) && lastPart);
- sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
+ sendSubmitPdu(submitPdu, sentIntent, deliveryIntent, destinationAddress);
}
protected void sendSubmitPdu(SmsMessage.SubmitPdu pdu,
- PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ PendingIntent sentIntent, PendingIntent deliveryIntent, String destAddr) {
if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {
if (sentIntent != null) {
try {
@@ -340,7 +340,7 @@
}
return;
}
- sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr);
}
/** {@inheritDoc} */
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 931c662..bfa2bb1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -241,7 +241,8 @@
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, destPort, data, (deliveryIntent != null));
if (pdu != null) {
- sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
+ destAddr);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
}
@@ -254,7 +255,8 @@
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
if (pdu != null) {
- sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
+ destAddr);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
}
@@ -276,7 +278,8 @@
message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
if (pdu != null) {
- sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
+ destinationAddress);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
new file mode 100644
index 0000000..3bb7c06
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE;
+
+/**
+ * Test cases for SMS short code pattern matching in SmsUsageMonitor.
+ */
+public class SmsUsageMonitorShortCodeTest extends AndroidTestCase {
+
+ private static final class ShortCodeTest {
+ final String countryIso;
+ final String address;
+ final int category;
+
+ ShortCodeTest(String countryIso, String destAddress, int category) {
+ this.countryIso = countryIso;
+ this.address = destAddress;
+ this.category = category;
+ }
+ }
+
+ /**
+ * List of short code test cases.
+ */
+ private static final ShortCodeTest[] sShortCodeTests = new ShortCodeTest[] {
+ new ShortCodeTest("al", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("al", "4321", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("al", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "15191", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "55500", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "55600", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("am", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("am", "101", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "102", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "103", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "222", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1111", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "9999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1121", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1141", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "3024", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("at", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("at", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("at", "0901234", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("at", "0900666266", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("au", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "180000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "190000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("au", "1900000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "19000000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("au", "19998882", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("az", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("az", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "87744", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "3301", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "3302", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9012", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9394", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "87744", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "93101", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "123456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("be", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("be", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("be", "567890", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("be", "8000", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("be", "6566", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("be", "7777", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("bg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("bg", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("bg", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1816", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1915", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1916", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1935", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "18423", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("by", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("by", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "3336", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "5013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "5014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "7781", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("ca", "911", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "+18005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "8005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "20000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "200000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "60999", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "88188", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("ch", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "123", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "3456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "98765", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "543", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "83111", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "234567", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("cn", "120", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cn", "1062503000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cn", "1065123456", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cn", "1066335588", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("cy", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cy", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cy", "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "654321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "7510", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("cz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cz", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cz", "9090150", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cz", "90901599", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cz", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("de", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("de", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("de", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "8888", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "11111", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "11886", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "22022", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "23300", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "3434", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "34567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "41414", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "55655", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "66766", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "66777", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "77677", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "80888", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "1232286", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("dk", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("dk", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("dk", "1259", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("dk", "16123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("dk", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ee", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ee", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ee", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "1259", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "15330", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17010", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "9034567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "34567890", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("es", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("es", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("es", "25165", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "27333", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "995399", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("fi", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("fi", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fi", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "123456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "17159", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "17163", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "0600123", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "070012345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("fr", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("fr", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fr", "34567", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fr", "45678", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fr", "81185", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fr", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("gb", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gb", "999", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gb", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("gb", "4567", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "45678", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "56789", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "79067", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "80079", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "654321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "7654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ge", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ge", "8765", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8012", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8889", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("gr", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gr", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("gr", "54321", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "19567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "19678", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("hu", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("hu", "012", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "0123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1784", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "2345", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "01234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "012345678", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "0123456789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1234567890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "0691227910", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "2345678901", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "01234567890", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ie", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ie", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ie", "50123", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ie", "51234", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("ie", "52345", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("ie", "57890", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ie", "67890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ie", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("il", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("il", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "4422", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "4545", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("it", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("it", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("it", "4567", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("it", "48000", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "45678", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "56789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "456789", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("kg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("kg", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4152", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4157", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4449", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("kz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("kz", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "9194", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "7790", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lt", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lt", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lt", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1381", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1394", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1645", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "123456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lu", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lu", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "12345", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "64747", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lu", "678901", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lv", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lv", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lv", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1819", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1863", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1874", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("mx", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("mx", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "7766", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "53035", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("my", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("my", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("my", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "32298", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "33776", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "345678", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("nl", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("nl", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("nl", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "4466", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "5040", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "23456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("no", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("no", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2201", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2226", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2227", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "234567", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("nz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("nz", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "3903", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "8995", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "23456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("pl", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("pl", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("pl", "7890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "34567", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "7910", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "74240", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "79866", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "92525", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("pt", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("pt", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("pt", "61000", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "62345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "68304", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "69876", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ro", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ro", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ro", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1263", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1288", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1314", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1380", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "7890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "12345", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ru", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ru", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "2097", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "3933", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "7781", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("se", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("se", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "72345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("se", "72999", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("se", "123456", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("sg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sg", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sg", "70000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "79999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "73800", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "74688", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("sg", "987654", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("si", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("si", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("si", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("si", "3838", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("si", "72999", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("sk", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sk", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("sk", "1234", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "6674", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "7604", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "72999", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("tj", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("tj", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "1171", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "4161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "4449", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ua", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ua", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "4448", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "7094", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "7540", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("us", "911", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "+18005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "8005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "20000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "200000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "20433", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "21472", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "23333", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "99807", CATEGORY_PREMIUM_SHORT_CODE),
+
+ // generic rules for other countries: 5 digits or less considered potential short code
+ new ShortCodeTest("zz", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("zz", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest(null, "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest(null, "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "112", CATEGORY_NOT_SHORT_CODE),
+ };
+
+ @SmallTest
+ public void testSmsUsageMonitor() {
+ SmsUsageMonitor monitor = new SmsUsageMonitor(getContext());
+ for (ShortCodeTest test : sShortCodeTests) {
+ assertEquals("country: " + test.countryIso + " number: " + test.address,
+ test.category, monitor.checkDestination(test.address, test.countryIso));
+ }
+ }
+}
diff --git a/tests/GridLayoutTest/AndroidManifest.xml b/tests/GridLayoutTest/AndroidManifest.xml
index 141e8fa..677220d 100644
--- a/tests/GridLayoutTest/AndroidManifest.xml
+++ b/tests/GridLayoutTest/AndroidManifest.xml
@@ -83,6 +83,13 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name="LayoutInsetsTest" android:label="LayoutInsetsTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
diff --git a/tests/GridLayoutTest/res/drawable/btn_default.xml b/tests/GridLayoutTest/res/drawable/btn_default.xml
new file mode 100644
index 0000000..c6cfda0
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/btn_default.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/my_btn_default_normal" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/my_btn_default_normal" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/my_btn_default_pressed" />
+ <item android:state_focused="true" android:state_enabled="true"
+ android:drawable="@drawable/my_btn_default_selected" />
+ <item android:state_enabled="true"
+ android:drawable="@drawable/my_btn_default_normal" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/my_btn_default_normal_disable_focused" />
+ <item
+ android:drawable="@drawable/my_btn_default_normal_disable" />
+</selector>
diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png
new file mode 100644
index 0000000..cd0b7d5
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png
Binary files differ
diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png
new file mode 100755
index 0000000..f4f01c7
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png
Binary files differ
diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable_focused.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable_focused.9.png
new file mode 100755
index 0000000..5376db2
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable_focused.9.png
Binary files differ
diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png
new file mode 100755
index 0000000..4312c27
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png
Binary files differ
diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png
new file mode 100755
index 0000000..06b7790
--- /dev/null
+++ b/tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png
Binary files differ
diff --git a/tests/GridLayoutTest/res/layout/grid7.xml b/tests/GridLayoutTest/res/layout/grid7.xml
index b9e58d7..0e5be0c 100644
--- a/tests/GridLayoutTest/res/layout/grid7.xml
+++ b/tests/GridLayoutTest/res/layout/grid7.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
- android:columnCount="2"
+
<Space
android:layout_row="0"
android:layout_column="0"
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/LayoutInsetsTest.java b/tests/GridLayoutTest/src/com/android/test/layout/LayoutInsetsTest.java
new file mode 100644
index 0000000..74daccc
--- /dev/null
+++ b/tests/GridLayoutTest/src/com/android/test/layout/LayoutInsetsTest.java
@@ -0,0 +1,59 @@
+package com.android.test.layout;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.Space;
+import android.widget.TextView;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
+import static android.widget.GridLayout.*;
+import static android.widget.GridLayout.FILL;
+import static android.widget.GridLayout.spec;
+
+public class LayoutInsetsTest extends Activity {
+ public static View create(Context context) {
+ GridLayout p = new GridLayout(context);
+ p.setUseDefaultMargins(true);
+ p.setAlignmentMode(ALIGN_BOUNDS);
+ p.setOrientation(VERTICAL);
+
+ {
+ TextView c = new TextView(context);
+ c.setTextSize(32);
+ c.setText("Email setup");
+ p.addView(c);
+ }
+ {
+ Button c = new Button(context);
+ c.setBackgroundResource(R.drawable.btn_default);
+ c.setText("Manual setup");
+ p.addView(c);
+ c.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Button b = (Button) v;
+ b.setEnabled(false);
+ }
+ });
+ }
+
+ return p;
+ }
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+ getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN;
+ setContentView(create(this));
+ }
+}
diff --git a/tests/SmokeTest/Android.mk b/tests/SmokeTest/Android.mk
index 0adfd4c..591a84e 100644
--- a/tests/SmokeTest/Android.mk
+++ b/tests/SmokeTest/Android.mk
@@ -6,11 +6,11 @@
# This builds "SmokeTestApp"
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
LOCAL_PACKAGE_NAME := SmokeTestApp
+LOCAL_SDK_VERSION := 8
+
include $(BUILD_PACKAGE)
# This builds "SmokeTest"
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/SmokeTest/tests/Android.mk b/tests/SmokeTest/tests/Android.mk
index 86bf23d..18e682e 100644
--- a/tests/SmokeTest/tests/Android.mk
+++ b/tests/SmokeTest/tests/Android.mk
@@ -4,8 +4,6 @@
# We only want this apk build for tests.
LOCAL_MODULE_TAGS := tests
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -17,5 +15,7 @@
LOCAL_INSTRUMENTATION_FOR := SmokeTestApp
+LOCAL_SDK_VERSION := 8
+
include $(BUILD_PACKAGE)
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ae01c75..5eac1f2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -44,7 +44,7 @@
private final static String TAG = "NotificationTestList";
NotificationManager mNM;
- Vibrator mVibrator = new Vibrator();
+ Vibrator mVibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
Handler mHandler = new Handler();
long mActivityCreateTime = System.currentTimeMillis();
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cbd591f..a4473c8 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1192,10 +1192,14 @@
if (targetSdk < 4) {
if (!hasWriteExternalStoragePermission) {
printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
+ printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
+ "'targetSdkVersion < 4'\n");
hasWriteExternalStoragePermission = true;
}
if (!hasReadPhoneStatePermission) {
printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
+ printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
+ "'targetSdkVersion < 4'\n");
}
}
@@ -1203,15 +1207,21 @@
// force them to always take READ_EXTERNAL_STORAGE as well.
if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
+ printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
+ "'requested WRITE_EXTERNAL_STORAGE'\n");
}
// Pre-JellyBean call log permission compatibility.
if (targetSdk < 16) {
if (!hasReadCallLogPermission && hasReadContactsPermission) {
printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
+ printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
+ "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
}
if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
+ printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
+ "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
}
}
@@ -1223,10 +1233,18 @@
*/
// Camera-related back-compatibility logic
if (!specCameraFeature) {
- if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
+ if (reqCameraFlashFeature) {
// if app requested a sub-feature (autofocus or flash) and didn't
// request the base camera feature, we infer that it meant to
printf("uses-feature:'android.hardware.camera'\n");
+ printf("uses-implied-feature:'android.hardware.camera'," \
+ "'requested android.hardware.camera.flash feature'\n");
+ } else if (reqCameraAutofocusFeature) {
+ // if app requested a sub-feature (autofocus or flash) and didn't
+ // request the base camera feature, we infer that it meant to
+ printf("uses-feature:'android.hardware.camera'\n");
+ printf("uses-implied-feature:'android.hardware.camera'," \
+ "'requested android.hardware.camera.autofocus feature'\n");
} else if (hasCameraPermission) {
// if app wants to use camera but didn't request the feature, we infer
// that it meant to, and further that it wants autofocus
@@ -1234,6 +1252,8 @@
printf("uses-feature:'android.hardware.camera'\n");
if (!specCameraAutofocusFeature) {
printf("uses-feature:'android.hardware.camera.autofocus'\n");
+ printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
+ "'requested android.permission.CAMERA permission'\n");
}
}
}
@@ -1245,16 +1265,22 @@
// if app either takes a location-related permission or requests one of the
// sub-features, we infer that it also meant to request the base location feature
printf("uses-feature:'android.hardware.location'\n");
+ printf("uses-implied-feature:'android.hardware.location'," \
+ "'requested a location access permission'\n");
}
if (!specGpsFeature && hasGpsPermission) {
// if app takes GPS (FINE location) perm but does not request the GPS
// feature, we infer that it meant to
printf("uses-feature:'android.hardware.location.gps'\n");
+ printf("uses-implied-feature:'android.hardware.location.gps'," \
+ "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
}
if (!specNetworkLocFeature && hasCoarseLocPermission) {
// if app takes Network location (COARSE location) perm but does not request the
// network location feature, we infer that it meant to
printf("uses-feature:'android.hardware.location.network'\n");
+ printf("uses-implied-feature:'android.hardware.location.network'," \
+ "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
}
// Bluetooth-related compatibility logic
@@ -1262,6 +1288,9 @@
// if app takes a Bluetooth permission but does not request the Bluetooth
// feature, we infer that it meant to
printf("uses-feature:'android.hardware.bluetooth'\n");
+ printf("uses-implied-feature:'android.hardware.bluetooth'," \
+ "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
+ "permission and targetSdkVersion > 4'\n");
}
// Microphone-related compatibility logic
@@ -1269,6 +1298,8 @@
// if app takes the record-audio permission but does not request the microphone
// feature, we infer that it meant to
printf("uses-feature:'android.hardware.microphone'\n");
+ printf("uses-implied-feature:'android.hardware.microphone'," \
+ "'requested android.permission.RECORD_AUDIO permission'\n");
}
// WiFi-related compatibility logic
@@ -1276,6 +1307,10 @@
// if app takes one of the WiFi permissions but does not request the WiFi
// feature, we infer that it meant to
printf("uses-feature:'android.hardware.wifi'\n");
+ printf("uses-implied-feature:'android.hardware.wifi'," \
+ "'requested android.permission.ACCESS_WIFI_STATE, " \
+ "android.permission.CHANGE_WIFI_STATE, or " \
+ "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
}
// Telephony-related compatibility logic
@@ -1283,6 +1318,8 @@
// if app takes one of the telephony permissions or requests a sub-feature but
// does not request the base telephony feature, we infer that it meant to
printf("uses-feature:'android.hardware.telephony'\n");
+ printf("uses-implied-feature:'android.hardware.telephony'," \
+ "'requested a telephony-related permission or feature'\n");
}
// Touchscreen-related back-compatibility logic
@@ -1292,11 +1329,15 @@
// Note that specTouchscreenFeature is true if the tag is present, regardless
// of whether its value is true or false, so this is safe
printf("uses-feature:'android.hardware.touchscreen'\n");
+ printf("uses-implied-feature:'android.hardware.touchscreen'," \
+ "'assumed you require a touch screen unless explicitly made optional'\n");
}
if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
// if app takes one of the telephony permissions or requests a sub-feature but
// does not request the base telephony feature, we infer that it meant to
printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
+ printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
+ "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
}
// Landscape/portrait-related compatibility logic
@@ -1306,9 +1347,13 @@
// orientation is required.
if (reqScreenLandscapeFeature) {
printf("uses-feature:'android.hardware.screen.landscape'\n");
+ printf("uses-implied-feature:'android.hardware.screen.landscape'," \
+ "'one or more activities have specified a landscape orientation'\n");
}
if (reqScreenPortraitFeature) {
printf("uses-feature:'android.hardware.screen.portrait'\n");
+ printf("uses-implied-feature:'android.hardware.screen.portrait'," \
+ "'one or more activities have specified a portrait orientation'\n");
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index e6c9351..44d28fa 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -229,6 +229,12 @@
}
@Override
+ public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+ int startHeight) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback) throws RemoteException {
// TODO Auto-generated method stub